zoukankan      html  css  js  c++  java
  • 题解 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;
    //}
    
  • 相关阅读:
    c#添加托盘图标右键后显示菜单选项
    mysql workbench修改字段报错
    mysql增删改查语句
    连接win7的mysql很慢解决方法
    uvm_cmdline_processor
    cadence help文件库调出指令 :cdnshelp
    设置开机自动运行vncserver
    Vector bit-select and part-select addressing verilog片选写法
    Verilog三段式状态机描述
    同步FIFO学习
  • 原文地址:https://www.cnblogs.com/dysyn1314/p/13040451.html
Copyright © 2011-2022 走看看