正睿省选模拟赛:两弹一星(组合数学,斯特林数,dp)

题目大意:
对于一个无向图 G G G,假设 G G G中有 x x x个联通块为树,那么 G G G的权值就为 x k x^k xk k k k给定)
给定 n , k n,k n,k n n n个点的所有无向图的权值之和
998244353 998244353 998244353取模
n ≤ 3 ∗ 1 0 4 , k ≤ 20 n\le 3*10^4,k\le 20 n3104,k20


s o l u t i o n solution solution
考虑一个 n 3 n^3 n3暴力, f [ i ] [ j ] f[i][j] f[i][j]表示 i i i个点,构成 j j j棵树的方案数
再统计一下 x x x个点没有树联通块的方案树就可以统计答案了
d p dp dp的过程可以 N T T NTT NTT优化到 n 2 l o g n n^2logn n2logn

∑ i i k \sum_i i^k iik可以很容易的想到用斯特林数展开

考虑一个图 G G G的贡献,假设他有 x x x个联通块为树
x k = ∑ j { k j } x j ‾ = ∑ j { k j } C x j ∗ j ! x^k=\sum_{j} \left\{ {k\atop j} \right\} x^{\underline j}\\ =\sum_j \left\{ {k\atop j} \right\} C_x^j * j! xk=j{jk}xj=j{jk}Cxjj!
考虑一下这个式子
C x j C_x^j Cxj相当于是从这 x x x个联通块中挑出 j j j个联通块,那就意味着我们可以把 x x x的贡献摊到所有大小为 j ≤ k j\le k jk的联通块上。
这样对于所有的图我们就可以一起算贡献了

考虑复杂度, f [ i ] [ j ] f[i][j] f[i][j]第二位只要求到 k k k
复杂度 O ( n k l o g n ) O(nklogn) O(nklogn)

#include<bits/stdc++.h>
using namespace std;
#define rep(i,j,k) for(int i = j;i <= k;++i)
int rd()
{
	int num = 0;char c = getchar();bool flag = true;
	while(c < '0'||c > '9') {if(c == '-') flag = false;c = getchar();}
	while(c >= '0' && c <= '9') num = num*10+c-48,c = getchar();
	if(flag) return num;else return -num;
}
const int p = 998244353,g = 3;
int n,k;
int f[30100][25],s[25][25];
int fac[30100],inv[30100],flen,inv_n;
int a[120100],b[120100],r[120100],w[120100];
inline int calc(int a,int b){return (a+=b)>=p?a-=p:a;}
inline int mul(int a,int b){return 1ll*a*b%p;}
inline int del(int a,int b){return (a-=b)<0?a+=p:a;}
inline int ksm(int a,int x){int now = 1;for(;x;x>>=1,a=1ll*a*a%p)if(x&1)now=1ll*now*a%p;return now;}
inline int min(int a,int b){return a<b?a:b;}
inline void swap(int &a,int &b){a^=b;b^=a;a^=b;}
inline int C(int i,int j){
    if(j > i) return 0;
    return (j==0||j==i)?1:mul(fac[i],mul(inv[j],inv[i-j]));
}
void fft(int *a,int f)
{
	w[0] = 1;
	rep(i,1,flen-1) if(i < r[i]) swap(a[i],a[r[i]]);
	for(int len = 2;len <= flen;len <<= 1)
	{
		int wn = ksm(g,(p-1)/len);
		if(f == 1) wn = ksm(wn,p-2);
		rep(i,1,len/2) w[i] = mul(w[i-1],wn);
		for(int st = 0;st < flen;st += len)
			rep(i,0,len/2-1)
			{
				int x = a[st+i],y = mul(w[i],a[st+i+len/2]);
				a[st+i] = calc(x,y);
				a[st+i+len/2] = del(x,y);
			}
	}
}
void work()
{
	rep(i,n,flen-1) a[i] = b[i] = 0;
	fft(a,1);fft(b,1);
	rep(i,0,flen-1) a[i] = mul(a[i],b[i]);
	fft(a,-1);
	rep(i,0,flen-1) a[i] = mul(a[i],inv_n);
}
int main()
{
	n = rd();k = rd();flen = 1;
	while(flen < 2*n-1) flen <<= 1;  inv_n = ksm(flen,p-2);
	rep(i,0,flen-1) r[i] = (r[i>>1]>>1) | ((i&1)?flen/2:0);
	s[0][0] = 1;  fac[0] = inv[0] = 1;
	rep(i,1,k) rep(j,1,i) s[i][j] = calc(s[i-1][j-1],mul(j,s[i-1][j]));
	rep(i,1,n) fac[i] = mul(fac[i-1],i),inv[i] = ksm(fac[i],p-2);
	rep(i,1,n) f[i][1] = i<2?1:ksm(i,i-2);
	rep(j,2,k)
	{ 
		rep(i,0,n-1) a[i] = mul(f[i+1][1],inv[i]);
		rep(i,0,n-1) b[i] = mul(f[i+1][j-1],inv[i+1]);
		work();
		rep(i,j,n) f[i][j] = mul(a[i-2],fac[i-1]);
	}
	int ans = 0;
	rep(i,1,n)
		rep(j,1,k)
			ans = calc(ans , mul( f[i][j] , mul( ksm(2,C(n-i,2)) , mul( C(n,i) , mul( fac[j] , s[k][j] ) ) ) ) );
	printf("%d\n",ans);
	return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值