题目大意
给一个(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;
//}