相当于是问有多少种排列,使得相邻的点之间没有边
考虑容斥
一张图中选了
d
条边,那么会形成
只要DP出形成
a
<script type="math/tex" id="MathJax-Element-128">a</script> 条链的方案数,然后NTT一下就可以了
// BEGIN CUT HERE
// END CUT HERE
#include <vector>
#include <list>
#include <map>
#include <set>
#include <deque>
#include <stack>
#include <bitset>
#include <algorithm>
#include <functional>
#include <numeric>
#include <utility>
#include <sstream>
#include <iostream>
#include <iomanip>
#include <cstdio>
#include <cmath>
#include <cstdlib>
#include <ctime>
#include <queue>
#include <assert.h>
#include <cstring>
using namespace std;
const int N=20,P=998244353;
int n,k,m,cnt,G[N];
struct edge{
int t,nx;
}E[N*N<<1];
inline void addedge(int x,int y){
E[++cnt].t=y; E[cnt].nx=G[x]; G[x]=cnt;
E[++cnt].t=x; E[cnt].nx=G[y]; G[y]=cnt;
}
int f[1<<14|5][N],g[1<<14|5][N],h[1<<14|5];
inline void add(int &x,int y){
(x+=y)%=P;
}
const int NN=6000010;
int w[2][NN],num,rev[NN],F[NN];
inline int Pow(int x,int y){
int ret=1;
for(;y;y>>=1,x=1LL*x*x%P) if(y&1) ret=1LL*ret*x%P;
return ret;
}
inline void Pre(int n){
num=n; int g=Pow(3,(P-1)/num);
w[0][0]=w[1][0]=1;
for(int i=1;i<num;i++) w[1][i]=1LL*w[1][i-1]*g%P;
for(int i=1;i<num;i++) w[0][i]=w[1][num-i];
}
inline void NTT(int *a,int n,int r){
for(int i=1;i<n;i++) if(rev[i]>i) swap(a[i],a[rev[i]]);
for(int i=1;i<n;i<<=1)
for(int j=0;j<n;j+=(i<<1))
for(int k=0;k<i;k++){
int x=a[j+k],y=1LL*w[r][num/(i<<1)*k]*a[j+k+i]%P;
a[j+k]=(x+y)%P; a[j+k+i]=(x-y+P)%P;
}
if(!r) for(int i=0,inv=Pow(n,P-2);i<n;i++) a[i]=1LL*a[i]*inv%P;
}
class HamiltonianPaths{
public:
int countPaths(int n, vector <int> a, vector <int> b, int k){
for(int i=0;i<a.size();i++)
addedge(a[i]+1,b[i]+1);
for(int i=1;i<=n;i++) f[1<<(i-1)][i]=1;
for(int S=1;S<(1<<n);S++)
for(int x=1;x<=n;x++){
if(!f[S][x]) continue;
for(int i=G[x];i;i=E[i].nx)
if(S>>(E[i].t-1)&1);else
add(f[S|(1<<(E[i].t-1))][E[i].t],f[S][x]);
add(h[S],f[S][x]);
}
g[0][0]=1;
for(int S=1;S<(1<<n);S++)
for(int i=1;i<=n;i++)
for(int s=S;s;s=(s-1)&S)
add(g[S][i],1LL*g[S^s][i-1]*h[s]%P);
int L=0,M; for(M=1;M<=n*k;M<<=1,L++); Pre(M<<=1);
for(int i=1;i<num;i++) rev[i]=(rev[i>>1]>>1)|((i&1)<<L);
for(int i=1,fac=1;i<=n;fac=1LL*fac*(++i)%P)
F[i]=1LL*g[(1<<n)-1][i]*Pow(fac,P-2)%P;
NTT(F,num,1);
for(int i=0;i<num;i++) F[i]=Pow(F[i],k);
NTT(F,num,0); int ans=0;
for(int i=1,fac=1;i<=n*k;fac=1LL*fac*(++i)%P)
if((n*k-i)&1)
ans=(ans-1LL*fac*F[i])%P;
else
ans=(ans+1LL*fac*F[i])%P;
return (ans+P)%P;
}
// BEGIN CUT HERE
public:
void run_test(int Case) { if ((Case == -1) || (Case == 0)) test_case_0(); if ((Case == -1) || (Case == 1)) test_case_1(); if ((Case == -1) || (Case == 2)) test_case_2(); if ((Case == -1) || (Case == 3)) test_case_3(); if ((Case == -1) || (Case == 4)) test_case_4(); if ((Case == -1) || (Case == 5)) test_case_5(); if ((Case == -1) || (Case == 6)) test_case_6(); }
private:
template <typename T> string print_array(const vector<T> &V) { ostringstream os; os << "{ "; for (typename vector<T>::const_iterator iter = V.begin(); iter != V.end(); ++iter) os << '\"' << *iter << "\","; os << " }"; return os.str(); }
void verify_case(int Case, const int &Expected, const int &Received) { cerr << "Test Case #" << Case << "..."; if (Expected == Received) cerr << "PASSED" << endl; else { cerr << "FAILED" << endl; cerr << "\tExpected: \"" << Expected << '\"' << endl; cerr << "\tReceived: \"" << Received << '\"' << endl; } }
void test_case_0() { int Arg0 = 3; int Arr1[] = {0,1}; vector <int> Arg1(Arr1, Arr1 + (sizeof(Arr1) / sizeof(Arr1[0]))); int Arr2[] = {1,2}; vector <int> Arg2(Arr2, Arr2 + (sizeof(Arr2) / sizeof(Arr2[0]))); int Arg3 = 2; int Arg4 = 152; verify_case(0, Arg4, countPaths(Arg0, Arg1, Arg2, Arg3)); }
void test_case_1() { int Arg0 = 12; int Arr1[] = {}; vector <int> Arg1(Arr1, Arr1 + (sizeof(Arr1) / sizeof(Arr1[0]))); int Arr2[] = {}; vector <int> Arg2(Arr2, Arr2 + (sizeof(Arr2) / sizeof(Arr2[0]))); int Arg3 = 10000; int Arg4 = 129246395; verify_case(1, Arg4, countPaths(Arg0, Arg1, Arg2, Arg3)); }
void test_case_2() { int Arg0 = 5; int Arr1[] = {0,1,2,3,4}; vector <int> Arg1(Arr1, Arr1 + (sizeof(Arr1) / sizeof(Arr1[0]))); int Arr2[] = {1,2,3,4,0}; vector <int> Arg2(Arr2, Arr2 + (sizeof(Arr2) / sizeof(Arr2[0]))); int Arg3 = 1; int Arg4 = 10; verify_case(2, Arg4, countPaths(Arg0, Arg1, Arg2, Arg3)); }
void test_case_3() { int Arg0 = 1; int Arr1[] = {}; vector <int> Arg1(Arr1, Arr1 + (sizeof(Arr1) / sizeof(Arr1[0]))); int Arr2[] = {}; vector <int> Arg2(Arr2, Arr2 + (sizeof(Arr2) / sizeof(Arr2[0]))); int Arg3 = 1; int Arg4 = 1; verify_case(3, Arg4, countPaths(Arg0, Arg1, Arg2, Arg3)); }
void test_case_4() { int Arg0 = 4; int Arr1[] = {1,2,3,2,3,3}; vector <int> Arg1(Arr1, Arr1 + (sizeof(Arr1) / sizeof(Arr1[0]))); int Arr2[] = {0,0,0,1,1,2}; vector <int> Arg2(Arr2, Arr2 + (sizeof(Arr2) / sizeof(Arr2[0]))); int Arg3 = 1; int Arg4 = 0; verify_case(4, Arg4, countPaths(Arg0, Arg1, Arg2, Arg3)); }
void test_case_5() { int Arg0 = 4; int Arr1[] = {1,2,3,2,3,3}; vector <int> Arg1(Arr1, Arr1 + (sizeof(Arr1) / sizeof(Arr1[0]))); int Arr2[] = {0,0,0,1,1,2}; vector <int> Arg2(Arr2, Arr2 + (sizeof(Arr2) / sizeof(Arr2[0]))); int Arg3 = 10006; int Arg4 = 33330626; verify_case(5, Arg4, countPaths(Arg0, Arg1, Arg2, Arg3)); }
void test_case_6() { int Arg0 = 14; int Arr1[] = {0,4,0,0,0,12,2,2,9,2,2,3,3,3,3,4,8,4,5,5,10,11,6,12,10,13,10,13,12,13,11}; vector <int> Arg1(Arr1, Arr1 + (sizeof(Arr1) / sizeof(Arr1[0]))); int Arr2[] = {2,0,5,8,11,1,5,6,2,10,12,4,5,9,13,7,4,13,6,7,5,5,8,7,8,8,9,9,10,10,13}; vector <int> Arg2(Arr2, Arr2 + (sizeof(Arr2) / sizeof(Arr2[0]))); int Arg3 = 50000; int Arg4 = 372837676; verify_case(6, Arg4, countPaths(Arg0, Arg1, Arg2, Arg3)); }
// END CUT HERE
};
// BEGIN CUT HERE
int main()
{
HamiltonianPaths ___test;
___test.run_test(0);
system("pause");
}
// END CUT HERE