【省选模拟】20/04/09 黑暗(生成函数)(多项式)

第一次考场自己搞生成函数,算是进步吧
好像可以用斯特林推,不过有一种无脑的方法

  • 题意: n n n 个点的所有无向图,每个图的贡献是连通块大小的 m m m 次方,多次询问 n ≤ 3 e 4 , m ≤ 15 n\le 3e4,m\le 15 n3e4,m15

  • 先预处理 F ( x ) F(x) F(x) 表示无向联通图的 E G F EGF EGF
    G m ( x ) G_m(x) Gm(x) 为答案的 E G F EGF EGF,那么有
    G m ( x ) = ∑ i ≥ 0 F ( x ) i ∗ i m i ! G_m(x)=\sum_{i\ge 0}\frac{F(x)^i*i^m}{i!} Gm(x)=i0i!F(x)iim
    考虑递推,稍微推导发现
    G m ( x ) = F ( x ) d G m − 1 ( x ) d F ( x ) , G 0 = exp ⁡ F ( x ) G_m(x)=F(x)\frac{\text{d}G_{m-1}(x)}{\text{d}F(x)},G_0=\exp F(x) Gm(x)=F(x)dF(x)dGm1(x),G0=expF(x)
    现在知道 d G d x \frac{\text{d}G}{\text{d}x} dxdG,那么我们可以预处理 d x d F \frac{\text{d}x}{\text{d}F} dFdx,然后就做完了, O ( n m log ⁡ n ) O(nm\log n) O(nmlogn),好像不用推式子,询问可以 O ( 1 ) O(1) O(1)

  • 题解的做法是考虑 n m = ∑ i = 1 m S m , i ( n i ) i ! n^m=\sum_{i=1}^mS_{m,i}\binom{n}{i}i! nm=i=1mSm,i(in)i!,然后让一个图贡献 ( n i ) i ! \binom{n}{i}i! (in)i!,设这个是 f i , n f_{i,n} fi,n,那么有 f i , n = ∑ j g j ( n j ) f i − 1 , n − j f_{i,n}=\sum_{j}g_j\binom{n}{j}f_{i-1,n-j} fi,n=jgj(jn)fi1,nj g g g 是无向联通图个数,转移系数是因为 i i i 个联通块是有序的
    不过这个询问要 O ( m ) O(m) O(m)

#include<bits/stdc++.h>
#define cs const
#define poly vector<int>
#define pb push_back
using namespace std;
int read(){
	int cnt=0, f=1; char ch=0;
	while(!isdigit(ch)){ ch=getchar(); if(ch=='-') f=-1; }
	while(isdigit(ch)) cnt=cnt*10+(ch-'0'), ch=getchar();
	return cnt*f;
}
typedef long long ll;
cs int Mod = 998244353;
int add(int a, int b){ return a + b >= Mod ? a + b - Mod : a + b; }
int dec(int a, int b){ return a - b < 0 ? a - b + Mod : a - b; }
int mul(int a, int b){ return 1ll * a * b % Mod; }
void Add(int &a, int b){ a = add(a,b); }
void Dec(int &a, int b){ a = dec(a,b); }
void Mul(int &a, int b){ a = mul(a,b); }
int ksm(int a, int b){ int as=1; for(;b;b>>=1,a=mul(a,a)) if(b&1) as=mul(as,a); return as; }
cs int K = 17, M = 1 << K | 5;
int fc[M], ifc[M], iv[M], T, n, m;
int up, bit; poly rev;
poly w[K+1];
poly F[16];
void NTT_init(){
	for(int i=1; i<=K; i++) w[i].resize(1<<(i-1)); 
	int wn=ksm(3,(Mod-1)/(1<<K)); w[K][0]=1;
	for(int i=1; i<(1<<(K-1)); i++) w[K][i]=mul(w[K][i-1],wn);
	for(int i=K-1;i;i--) for(int j=0; j<(1<<(i-1)); j++) w[i][j]=w[i+1][j<<1];
	iv[0]=iv[1]=1; for(int i=2; i<=(1<<K); i++) iv[i]=mul(Mod-Mod/i,iv[Mod%i]);
}
void Fac_init(int n){
	fc[0]=fc[1]=ifc[0]=ifc[1]=1;
	for(int i=2; i<=n; i++) fc[i]=mul(fc[i-1],i);
	for(int i=2; i<=n; i++) ifc[i]=mul(ifc[i-1],iv[i]);
}
void init(int deg){
	up=1; bit=0; while(up<deg) up<<=1,++bit; rev.resize(up);
	for(int i=0; i<up; i++) rev[i]=(rev[i>>1]>>1)|((i&1)<<(bit-1));
}
void NTT(poly &a, int typ=1){
	for(int i=0; i<up; i++) if(i<rev[i]) swap(a[i],a[rev[i]]);
	for(int i=1,l=1; i<up; i<<=1,++l)
	for(int j=0; j<up; j+=(i<<1))
	for(int k=0; k<i; k++){
		int x=a[k+j], y=mul(w[l][k],a[k+j+i]);
		a[k+j]=add(x,y); a[k+j+i]=dec(x,y);
	}
	if(typ==-1){
		reverse(a.begin()+1,a.end());
		for(int i=0; i<up; i++) Mul(a[i],iv[up]);
	}
}
poly operator * (poly a, poly b){
	int deg=a.size()+b.size()-1; 
	if(a.size()<=16||b.size()<=16){
		poly c(deg,0); 
		for(int i=0; i<(int)a.size(); i++)
		for(int j=0; j<(int)b.size(); j++)
		Add(c[i+j],mul(a[i],b[j])); return c;
	}
	init(deg); a.resize(up); b.resize(up); NTT(a); NTT(b);
	for(int i=0; i<up; i++) Mul(a[i],b[i]); NTT(a,-1);
	a.resize(deg); return a;
}
poly inv(poly a, int deg){
	poly b(1,ksm(a[0],Mod-2)),c;
	for(int lim=2; (lim>>1)<deg; lim<<=1){
		c=a; c.resize(lim); init(lim<<1);
		c.resize(up); b.resize(up); NTT(c); NTT(b);
		for(int i=0; i<up; i++) Mul(b[i],dec(2,mul(b[i],c[i])));
		NTT(b,-1); b.resize(lim); 
	} b.resize(deg); return b;
}
poly integ(poly a){
	a.pb(0);
	for(int i=a.size()-1; i>=1; i--) a[i]=mul(a[i-1],iv[i]);
	a[0]=0; return a;
}
poly deriv(poly a){
	for(int i=0; i+1<(int)a.size(); i++) a[i]=mul(a[i+1],i+1);
	a.pop_back(); return a;
}
poly ln(poly a, int deg){
	a=integ(inv(a,deg)*deriv(a)); a.resize(deg);
	return a;
}
void work(int n, int m){
	poly f; for(int i=0; i<=n; i++){
		int w=((ll)i*(i-1)>>1)%(Mod-1);
		f.pb(mul(ksm(2,w),ifc[i]));
	} F[0]=f; f=ln(f,n+1);
	poly df=inv(deriv(f),n+1);
	for(int i=1; i<=m; i++){
		F[i]=f*deriv(F[i-1]); F[i].resize(n+1);
		F[i]=F[i]*df;
	} 
}
int main(){
	freopen("dark.in","r",stdin);
	freopen("dark.out","w",stdout);
	NTT_init(); 
	Fac_init(3e4+5);
	work(3e4+3,15);
	T=read(); while(T--){
		n=read(), m=read();
		assert(n<(int)F[m].size());
		cout<<mul(fc[n],F[m][n])<<'\n';
	}
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

FSYo

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值