题解 TopCoder14250 HamiltonianPaths

题目链接

题目大意

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

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

(kleq 14,nleq 5 imes 10^4)

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

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

(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)条边是我们强制不合法的(连通块内的)。

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

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

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

复杂度(O(2^kk^2+3^kk+nklog {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;
//}
原文地址:https://www.cnblogs.com/dysyn1314/p/13040451.html