【校内模拟】黑暗(第二类斯特林数)(多项式求逆)

简要题意:here


题解:

c ( G ) c(G) c(G) 表示图 G G G 的联通块数量,首先利用第二类斯特林数转成下降幂:

n m = ∑ k = 0 m S m , k n k ‾ n^m=\sum_{k=0}^mS_{m,k}n^{\underline k} nm=k=0mSm,knk

考虑 c ( G ) k ‾ c(G)^{\underline k} c(G)k 的意义,就是有序选择 k k k 个连通块的方案数,这部分有点像 小C的岛屿 的处理方式。

f i , n f_{i,n} fi,n 表示所有 n n n 个点的图有序选择 i i i 个连通块的方案数之和。

g i g_i gi 表示 i i i 个点的连通图个数,这个多项式求逆算一下就行了。

f f f 的转移也很明显 f i , n = ∑ k = 1 n g k ( n k ) f i − 1. n − k f_{i,n}=\sum_{k=1}^ng_k{n\choose k}f_{i-1.n-k} fi,n=k=1ngk(kn)fi1.nk

由于 m m m 很小,对于所有要用的 f f f 暴力处理即可。


代码:

#include<bits/stdc++.h>
#define ll long long
#define re register
#define cs const

using std::cerr;
using std::cout;

cs int mod=998244353;
inline int add(int a,int b){return a+b>=mod?a+b-mod:a+b;}
inline int dec(int a,int b){return a-b<0?a-b+mod:a-b;}
inline int mul(int a,int b){ll r=(ll)a*b;return r>=mod?r%mod:r;}
inline void Inc(int &a,int b){a+=b-mod;a+=a>>31&mod;}
inline void Dec(int &a,int b){a-=b;a+=a>>31&mod;}
inline void Mul(int &a,int b){a=mul(a,b);} 
inline int po(int a,int b){int r=1;for(;b;b>>=1,Mul(a,a))if(b&1)Mul(r,a);return r;}
inline void ex_gcd(int a,int b,int &x,int &y){
	if(!b){x=1,y=0;return;}ex_gcd(b,a%b,y,x);y-=a/b*x;
}inline int Inv(int a){static int x,y;ex_gcd(mod,a,y,x);return x+(x>>31&mod);}

cs int bit=17,SIZE=1<<bit|7;

int r[SIZE],*w[bit+1];

void init_omega(){
	for(int re i=1;i<=bit;++i)
		w[i]=new int[1<<(i-1)];
	w[bit][0]=1;int wn=po(3,(mod-1)>>bit);
	for(int re i=1;i<(1<<(bit-1));++i)
		w[bit][i]=mul(w[bit][i-1],wn);
	for(int re j=bit-1;j;--j)
		for(int re i=0;i<(1<<(j-1));++i)
			w[j][i]=w[j+1][i<<1];
}

int fac[SIZE],ifc[SIZE],inv[SIZE];

void init_fac(){
	fac[0]=fac[1]=1;
	ifc[0]=ifc[1]=1;
	inv[0]=inv[1]=1;
	for(int re i=2;i<SIZE;++i){
		fac[i]=mul(fac[i-1],i);
		inv[i]=mul(inv[mod%i],mod-mod/i);
		ifc[i]=mul(ifc[i-1],inv[i]);
	}
}

int len,inv_len;

void init_len(int l){
	len=l;inv_len=inv[l];
	for(int re i=1;i<l;++i)
		r[i]=r[i>>1]>>1|((i&1)?l>>1:0);
}

void DFT(int *A){
	for(int re i=1;i<len;++i)
		if(i<r[i])std::swap(A[i],A[r[i]]);
	for(int re i=1,d=1;i<len;i<<=1,++d)
		for(int re j=0;j<len;j+=i<<1)
		if(i<8){
			for(int re k=0;k<i;++k){
				int &t1=A[j+k],&t2=A[i+j+k];
				int t=mul(t2,w[d][k]);
				t2=dec(t1,t);Inc(t1,t);
			}
		}else {
#define work(p)	\
{				\
	int &t1=A[j+k+p],&t2=A[i+j+k+p];	\
	int t=mul(t2,w[d][k+p]);			\
	t2=dec(t1,t);Inc(t1,t);			\
}
			for(int re k=0;k<i;k+=8){
				work(0);work(1);work(2);work(3);
				work(4);work(5);work(6);work(7);
			}
		}
}void IDFT(int *A){
	DFT(A);std::reverse(A+1,A+len);
	for(int re i=0;i<len;++i)Mul(A[i],inv_len);
}

typedef std::vector<int> Poly;
inline void DFT(Poly &A){DFT(&A[0]);}
inline void IDFT(Poly &A){IDFT(&A[0]);}

void Inv(int *a,int n,int *b){
	static int c[SIZE];b[0]=Inv(a[0]);
	for(int re l=4;(l>>2)<n;l<<=1){
		memcpy(c,a,sizeof(int)*(l>>1));
		memset(c+(l>>1),0,sizeof(int)*(l>>1));
		memset(b+(l>>2),0,sizeof(int)*(l-(l>>2)));
		init_len(l);DFT(b),DFT(c);
		for(int re i=0;i<l;++i)
			Mul(b[i],dec(2,mul(b[i],c[i])));
		IDFT(b);
	}
}

cs int M=20;
int S[M][M];
int f[M][SIZE];
int G[SIZE];

int A[SIZE],B[SIZE],C[SIZE];

void Main(){
	init_omega();init_fac();
	S[0][0]=1;
	for(int re i=1;i<M;++i)
		for(int re j=1;j<M;++j)
			S[i][j]=add(S[i-1][j-1],mul(S[i-1][j],j));
	int n=1<<15;init_len(n+n);
	for(int re i=1;i<n;++i){
		int pw=po(2,(i-1ll)*i/2%(mod-1));
		A[i]=mul(pw,ifc[i-1]);
		B[i]=mul(pw,ifc[i]);
	}B[0]=1;Inv(B,n,C);
	memset(C+n,0,sizeof(int)*n);
	DFT(A),DFT(C);
	for(int re i=0;i<len;++i)
		G[i]=mul(A[i],C[i]);
	IDFT(G);
	memset(G+n,0,sizeof(int)*n);
	for(int re i=1;i<n;++i)
		Mul(G[i],inv[i]);
	DFT(G);
	memcpy(f[0],B,sizeof(int)*n);
	for(int re i=1;i<=15;++i){
		memcpy(A,f[i-1],sizeof(int)*len);
		DFT(A);for(int re j=0;j<len;++j)
			f[i][j]=mul(A[j],G[j]);
		IDFT(f[i]);memset(f[i]+n,0,sizeof(int)*n);
	}
	int T;scanf("%d",&T);
	while(T--){
		int n,m,ans=0;
		scanf("%d%d",&n,&m);
		for(int re i=0;i<=m;++i)
			Inc(ans,mul(S[m][i],mul(f[i][n],fac[n])));
		cout<<ans<<"\n";
	}
}

inline void file(){
#ifdef zxyoi
	freopen("dark.in","r",stdin);
#else
	freopen("dark.in","r",stdin);
	freopen("dark.out","w",stdout);
#endif
}signed main(){file();Main();return 0;} 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值