#2708. 黑暗(dark)
题目描述
n个点的无向图,每条边都可能存在,一个图的权值是连通块个数的m次方,求所有可能的图的权值和,答案对998244353取模。
数据范围
$T \le 1000,n \le 30000,m \le 15$
题解
设 $h_{n,i}$ 表示 $n$ 个点的图有 $i$ 个连通块的方案数,答案可以写成$$\sum_{k=1}^nh_{n,k}k^m$$
化式子,得 $$\sum_{k=1}^nh_{n,k}\sum_{i=1}^m\{_i^m\}(_i^k)i!$$
其中 $\{_i^m\}$ 代表第二类斯特林数。将 $i$ 提前,得$$\sum_{i=1}^m\{_i^m\}i!\sum_{k=1}^nh_{n,k}(_i^k)$$
设 $f_{n,i}=\sum_{k=1}^nh_{n,k}(_i^k)$ , 则上述式子为$$\sum_{i=1}^m\{_i^m\}i!f_{n,i}$$
接着我们化简 $f_{n,i}$ , 可以枚举编号为1的点所在的连通块大小,我们预处理 $g_i$ 表示i个点的图是连通的的方案数,这个就是城市规划那题要求的,我们可以得到$$f_{n,i}=\sum_{k=1}^n(_i^k)\sum_{j=1}^{n-k+1}(_{j-1}^{n-1})g_jh_{n-j,k-1}$$
由组合数的递推式得$$f_{n,i}=\sum_{j=1}^n(_{j-1}^{n-1})g_j\sum_{k=1}^{n-j+1}((_{i-1}^{k-1})+(_i^{k-1}))h_{n-j,k-1}$$
用 $k$ 取代 $k-1$ ,得$$f_{n,i}=\sum_{j=1}^n(_{j-1}^{n-1})g_j(f_{n-j,i-1}+f_{n-j,i})$$
于是我们可以得到$$f_{i,n}=(n-1)!\sum_{j=1}^n\frac{g_j}{(j-1)!}\frac{f_{i-1,n-j}}{(n-j)!}+\frac{g_j}{(j-1)!}\frac{f_{i,n-j}}{(n-j)!}$$
于是我们就可以分治 $Ntt$ 求出 $f_i(x)$ 效率: $O(nlog^2n)$
代码
#include <bits/stdc++.h> using namespace std; const int M=16,P=998244353,N=30001,Z=N<<2; int ans,T,n,m,s[M][M],jc[N],ny[N],g[Z],b[Z],c[Z],d[Z],A[Z],B[Z],f[M][N],t,p,G[2]={3,(P+1)/3},r[Z]; int X(int x){if (x>=P) x-=P;return x;} int K(int x,int y){ int A=1; for (;y;y>>=1,x=1ll*x*x%P) if (y&1) A=1ll*A*x%P; return A; } void Ntt(int *g,bool o){ for (int i=0;i<t;i++) if (i<r[i]) swap(g[i],g[r[i]]); for (int wn,i=1;i<t;i<<=1){ wn=K(G[o],(P-1)/(i<<1)); for (int x,y,j=0;j<t;j+=(i<<1)) for (int w=1,k=0;k<i;k++,w=1ll*w*wn%P) x=g[j+k],y=1ll*w*g[i+j+k]%P, g[j+k]=X(x+y),g[i+j+k]=X(x-y+P); } if (o) for (int i=0,v=K(t,P-2);i<t;i++) g[i]=1ll*v*g[i]%P; } void pre(int l){ for (t=1,p=0;t<l;t<<=1,p++); for (int i=0;i<t;i++) r[i]=(r[i>>1]>>1)|((i&1)<<(p-1)); } void getinv(int *a,int *b,int l){ if (l==1){ b[0]=K(a[0],P-2);return; } getinv(a,b,(l+1)>>1); for (int i=0;i<l;i++) A[i]=a[i],B[i]=b[i]; pre(l+l);Ntt(A,0);Ntt(B,0); for (int i=0;i<t;i++) A[i]=1ll*A[i]*B[i]%P*B[i]%P; Ntt(A,1);for (int i=0;i<l;i++) b[i]=X(X(b[i]+b[i])-A[i]+P); for (int i=0;i<t;i++) A[i]=B[i]=0; } void solve(int *a,int l,int r){ if (l==r){ a[l]=X(a[l]+d[l]); if (l) a[l]=1ll*a[l]*jc[l-1]%P; return; } int mid=(l+r)>>1;solve(a,l,mid); for (int i=0;i<=mid-l;i++) b[i]=1ll*a[l+i]*ny[l+i]%P; for (int i=0;i<r-l;i++) c[i]=1ll*g[i+1]*ny[i]%P; pre(mid-l+1+r-l);Ntt(b,0);Ntt(c,0); for (int i=0;i<t;i++) b[i]=1ll*b[i]*c[i]%P; Ntt(b,1);for (int i=mid+1;i<=r;i++) a[i]=X(a[i]+b[i-l-1]); for (int i=0;i<t;i++) b[i]=c[i]=0; solve(a,mid+1,r); } int main(){ s[0][0]=1; for (int i=1;i<M;i++) for (int j=1;j<=i;j++) s[i][j]=(s[i-1][j-1]+1ll*s[i-1][j]*j%P)%P; jc[0]=1; for (int i=1;i<N;i++) jc[i]=1ll*i*jc[i-1]%P; ny[N-1]=K(jc[N-1],P-2); for (int i=N-1;i;i--) ny[i-1]=1ll*i*ny[i]%P; for (int i=0;i<N;i++) g[i]=1ll*K(2,(1ll*i*(i-1)/2)%(P-1))*ny[i]%P; getinv(g,b,N); for (int i=0;i<N;i++) g[i]=1ll*K(2,(1ll*i*(i-1)/2)%(P-1))*ny[i-1]%P; pre(N+N);Ntt(g,0);Ntt(b,0); for (int i=0;i<t;i++) g[i]=1ll*g[i]*b[i]%P,b[i]=0; Ntt(g,1);for (int i=1;i<N;i++) g[i]=1ll*g[i]*jc[i-1]%P; for (int i=0;i<M;i++){ if (!i){d[0]=g[0]=1;goto out;} for (int j=1;j<N;j++) b[j]=1ll*g[j]*ny[j-1]%P, c[N-j-1]=1ll*f[i-1][N-j-1]*ny[N-j-1]%P; pre(N+N);Ntt(b,0),Ntt(c,0); for (int j=0;j<t;j++) d[j]=1ll*b[j]*c[j]%P,b[j]=c[j]=0; Ntt(d,1);out:;solve(f[i],0,N-1); } for (scanf("%d",&T);T--;){ scanf("%d%d",&n,&m);ans=0; for (int i=1;i<=m;i++) ans=X(ans+1ll*s[m][i]*f[i][n]%P*jc[i]%P); printf("%d\n",ans); } return 0; }