题解 TopCoder14250 HamiltonianPaths

题目链接

题目大意

给一个\(k\)个点的无向图\(G1\),你将它复制了\(n\)份,得到一个图\(G2\),然后取补图得到\(G2'\)

\(G2'\)中哈密尔顿路径数(不是回路数),对\(998244353\)取模。

\(k\leq 14,n\leq 5\times 10^4\)

条件相当于求不能走原图里的边的方案数。考虑容斥,转化为求必须走至少\(i\)条原图里边的方案数。

\(k\)很小,考虑状压。设\(g_{s,i}\)表示走了\(G1\)的点集\(s\),不能走的边构成了\(i\)条链的方案数(一定是链,否则不是哈密尔顿回路)。所以我们先求一个\(f_{s,i}\)表示点集\(s\)里的点构成一条链,且链的一端在\(i\)的方案数。这样就有\(g_{s,i}=\displaystyle\sum_{t\subseteq s,t\neq \varnothing}g_{s\setminus t,i-1}\big(\sum_{j\in t}f_{t,j}\big)\)

\(f\)\(O(2^kk^2)\),预处理\(f_t\)的和后,求\(g\)的复杂度是\(O(3^kk)\)

现在我们求出了把\(G1\)缩成\(i\)个连通块的方案数\(g_{(1<<k)-1,i}\)。根据前面DP的定义,这里每个连通块其实都是一条链。我们要在\(G2\)中取出若干个这样的连通块,此时这些连通块间是一个完全图。因为根据容斥,非联通块里的边是可以随便选的,既可以选不合法边(原图里的),也可以选合法边(补图里的),这就显然是完全图了。

在完全图中,设有\(x\)个连通块,则它们间哈密尔顿路径的数量就是\(x!\)。容斥时对答案的贡献系数是\((-1)^{nk-x}\),因为共有\(nk-1\)条边,\(x\)个连通块间的\(x-1\)条边是随便选的,剩下的\(nk-x\)条边是我们强制不合法的(连通块内的)。

所以现在我们要做的就是对每个\(1\leq x\leq nk\),求出把\(G2\)缩成\(x\)个联通块的方案数。

\(G2\)就是\(n\)\(G1\),而\(G1\)内缩成\(x\)个连通块的方案数我们已经求出,就是\(g_{(1<<k)-1,x}\)。相当于我们要做\(n\)次背包,也可以看做是\(n\)自己卷自己的卷积。我们可以先把这个多项式的点值表示求出来,对每个点值做快速幂,再IDFT回去即可。这个多项式的长度显然是\(nk\),所以复杂度\(O(nk\log {nk})\)

设卷积后的数组是\(a\),则答案就是\(\displaystyle\sum_{x=1}^{nk}(-1)^{nk-x}\cdot (x!)\cdot a_x\)

复杂度\(O(2^kk^2+3^kk+nk\log {nk})\)

参考代码:

//problem:
#include <bits/stdc++.h>
using namespace std;

class HamiltonianPaths{
private:
	#define pb push_back
	#define mk make_pair
	#define lob lower_bound
	#define upb upper_bound
	#define fst first
	#define scd second
	
	typedef long long ll;
	typedef unsigned long long ull;
	typedef pair<int,int> pii;
	typedef pair<ll,ll> pll;
	
	const int MOD=998244353;
	inline int mod1(int x){return x<MOD?x:x-MOD;}
	inline int mod2(int x){return x<0?x+MOD:x;}
	inline int pow_mod(int x,int i){int y=1;while(i){if(i&1)y=(ll)y*x%MOD;x=(ll)x*x%MOD;i>>=1;}return y;}
	
	int fac[50005*15],invf[50005*15];
	vector<int>G[14];
	int m,f[1<<14][14],sf[1<<14],g[1<<14][14*14+5];
	int a[50005*15*2],rev[50005*15*2];
	void ntt(int *a,int n,int flag){
		for(int i=0;i<n;++i)if(i<rev[i])swap(a[i],a[rev[i]]);
		for(int i=1;i<n;i<<=1){
			int T=pow_mod(3,(MOD-1)/(i<<1));
			if(flag==-1)T=pow_mod(T,MOD-2);
			for(int j=0;j<n;j+=(i<<1)){
				for(int k=0,t=1;k<i;++k,t=(ll)t*T%MOD){
					int Nx=a[j+k],Ny=(ll)a[i+j+k]*t%MOD;
					a[j+k]=mod1(Nx+Ny);
					a[i+j+k]=mod2(Nx-Ny);
				}
			}
		}
		if(flag==-1){
			int invn=pow_mod(n,MOD-2);
			for(int i=0;i<n;++i)a[i]=(ll)a[i]*invn%MOD;
		}
	}
public:
	int countPaths(int n,vector<int> u,vector<int> v,int x){
		fac[0]=1;for(int i=1;i<=n*x;++i)fac[i]=(ll)fac[i-1]*i%MOD;
		invf[n*x]=pow_mod(fac[n*x],MOD-2);
		for(int i=n*x-1;i>=0;--i)invf[i]=(ll)invf[i+1]*(i+1)%MOD;assert(invf[0]==1);
		m=u.size();
		for(int i=0;i<m;++i)G[u[i]].pb(v[i]),G[v[i]].pb(u[i]);
		for(int i=0;i<n;++i)f[1<<i][i]=1;
		for(int s=1;s<(1<<n);++s){
			for(int i=0;i<n;++i)if((s>>i)&1){
				for(int j=0;j<(int)G[i].size();++j)if(!((s>>G[i][j])&1)){
					f[s^(1<<G[i][j])][G[i][j]]=mod1(f[s^(1<<G[i][j])][G[i][j]]+f[s][i]);
				}
			}
		}
		for(int s=1;s<(1<<n);++s){
			for(int i=0;i<n;++i)sf[s]=mod1(sf[s]+f[s][i]);
		}
		g[0][0]=1;
		for(int s=1;s<(1<<n);++s){
			for(int i=1;i<=n;++i){
				for(int t=s;t;t=((t-1)&s)){
					g[s][i]=mod1(g[s][i]+(ll)g[s^t][i-1]*sf[t]%MOD);
				}
			}
		}
		for(int i=1;i<=n;++i)a[i]=(ll)g[(1<<n)-1][i]*invf[i]%MOD;
		int lim=1,ct=0;
		while(lim<=n*x)lim<<=1,ct++;
		for(int i=0;i<lim;++i)rev[i]=(rev[i>>1]>>1)|((i&1)<<(ct-1));
		ntt(a,lim,1);
		for(int i=0;i<lim;++i)a[i]=pow_mod(a[i],x);
		ntt(a,lim,-1);
		int ans=0;
		for(int i=0;i<=n*x;++i){
			if((n*x-i)&1)ans=mod2(ans-(ll)a[i]*fac[i]%MOD);
			else ans=mod1(ans+(ll)a[i]*fac[i]%MOD);
		}
		return ans;
	}
};

//HamiltonianPaths Solver;
//int main() {
//	int n,m,x;vector<int>u,v;
//	cin>>n>>m;
//	for(int i=0;i<m;++i){int t;cin>>t;u.pb(t);}
//	for(int i=0;i<m;++i){int t;cin>>t;v.pb(t);}
//	cin>>x;
//	cout<<Solver.countPaths(n,u,v,x)<<endl;
//	return 0;
//}
posted @ 2020-06-03 22:20  duyiblue  阅读(164)  评论(0编辑  收藏  举报