#2708. 黑暗(dark)

题目描述

n个点的无向图,每条边都可能存在,一个图的权值是连通块个数的m次方,求所有可能的图的权值和,答案对998244353取模。

数据范围

T ≤ 1000 , n ≤ 30000 , m ≤ 15 T \le 1000,n \le 30000,m \le 15 T1000,n30000,m15

题解

h n , i h_{n,i} hn,i 表示 n n n 个点的图有 i i i 个连通块的方案数,答案可以写成 ∑ k = 1 n h n , k k m \sum_{k=1}^nh_{n,k}k^m k=1nhn,kkm
化式子,得 ∑ k = 1 n h n , k ∑ i = 1 m { i m } ( i k ) i ! \sum_{k=1}^nh_{n,k}\sum_{i=1}^m\{_i^m\}(_i^k)i! k=1nhn,ki=1m{im}(ik)i!
其中 { i m } \{_i^m\} {im} 代表第二类斯特林数。将 i i i 提前,得 ∑ i = 1 m { i m } i ! ∑ k = 1 n h n , k ( i k ) \sum_{i=1}^m\{_i^m\}i!\sum_{k=1}^nh_{n,k}(_i^k) i=1m{im}i!k=1nhn,k(ik)
f n , i = ∑ k = 1 n h n , k ( i k ) f_{n,i}=\sum_{k=1}^nh_{n,k}(_i^k) fn,i=k=1nhn,k(ik) , 则上述式子为 ∑ i = 1 m { i m } i ! f n , i \sum_{i=1}^m\{_i^m\}i!f_{n,i} i=1m{im}i!fn,i
接着我们化简 f n , i f_{n,i} fn,i , 可以枚举编号为1的点所在的连通块大小,我们预处理 g i g_i gi 表示i个点的图是连通的的方案数,这个就是城市规划那题要求的,我们可以得到 f n , i = ∑ k = 1 n ( i k ) ∑ j = 1 n − k + 1 ( j − 1 n − 1 ) g j h n − j , k − 1 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} fn,i=k=1n(ik)j=1nk+1(j1n1)gjhnj,k1
由组合数的递推式得 f n , i = ∑ j = 1 n ( j − 1 n − 1 ) g j ∑ k = 1 n − j + 1 ( ( i − 1 k − 1 ) + ( i k − 1 ) ) h 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} fn,i=j=1n(j1n1)gjk=1nj+1((i1k1)+(ik1))hnj,k1
k k k 取代 k − 1 k-1 k1 ,得 f n , i = ∑ j = 1 n ( j − 1 n − 1 ) g j ( f n − j , i − 1 + f n − j , i ) f_{n,i}=\sum_{j=1}^n(_{j-1}^{n-1})g_j(f_{n-j,i-1}+f_{n-j,i}) fn,i=j=1n(j1n1)gj(fnj,i1+fnj,i)
于是我们可以得到 f i , n = ( n − 1 ) ! ∑ j = 1 n g j ( j − 1 ) ! f i − 1 , n − j ( n − j ) ! + g j ( j − 1 ) ! f i , n − j ( n − j ) ! 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)!} fi,n=(n1)!j=1n(j1)!gj(nj)!fi1,nj+(j1)!gj(nj)!fi,nj
于是我们就可以分治 N t t Ntt Ntt 求出 f i ( x ) f_i(x) fi(x) 效率: O ( n l o g 2 n ) O(nlog^2n) O(nlog2n)

代码
#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;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值