【校內模擬】黑暗(第二類斯特林數)(多項式求逆)

簡要題意:here


題解:

c(G)c(G) 表示圖 GG 的聯通塊數量,首先利用第二類斯特林數轉成下降冪:

nm=k=0mSm,knkn^m=\sum_{k=0}^mS_{m,k}n^{\underline k}

考慮 c(G)kc(G)^{\underline k} 的意義,就是有序選擇 kk 個連通塊的方案數,這部分有點像 小C的島嶼 的處理方式。

fi,nf_{i,n} 表示所有 nn 個點的圖有序選擇 ii 個連通塊的方案數之和。

gig_i 表示 ii 個點的連通圖個數,這個多項式求逆算一下就行了。

ff 的轉移也很明顯 fi,n=k=1ngk(nk)fi1.nkf_{i,n}=\sum_{k=1}^ng_k{n\choose k}f_{i-1.n-k}

由於 mm 很小,對於所有要用的 ff 暴力處理即可。


代碼:

#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;} 
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章