【LOJ #6363】「地底蔷薇」(拉格朗日反演)(多项式全家桶)

传送门

  • 神题 o r z orz orz
  • 首先可以求出 H ( x ) H(x) H(x) 表示无向联通图个数,并且我们为每个图定一个根
    考虑断掉根所在的若干个点双的所有边,我们搞出一个连通块的 E G F EGF EGF exp \text{exp} exp 就可以得到若干个点双,考虑断掉点双内所有边后剩下的联通块个数就是点双大小(除根),于是我们定义 b i b_i bi 为大小为 i + 1 i+1 i+1 的点双的方案数, B ( x ) B(x) B(x) 为其 E G F EGF EGF,并有单个点双的贡献: ∑ i ≥ 1 b i i ! H ( x ) i = B ( H ( x ) ) \sum_{i\ge 1}\frac{b_i}{i!}H(x)^i=B(H(x)) i1i!biH(x)i=B(H(x))
    所以有等式:
    H ( x ) = x exp ⁡ B ( H ( x ) ) H ( x ) exp ⁡ B ( H ( x ) ) = x H(x)=x\exp{B(H(x))}\\ \frac{H(x)}{\exp B(H(x))}=x H(x)=xexpB(H(x))expB(H(x))H(x)=x
    知道 H ( x ) H(x) H(x) 要求 B ( x ) B(x) B(x),我们构造 H ( x ) H(x) H(x) 的反函数 H − 1 ( x ) = x exp ⁡ B ( x ) H^{-1}(x)=\frac{x}{\exp B(x)} H1(x)=expB(x)x
    那么有 B ( x ) = ln ⁡ x H − 1 ( x ) B(x)=\ln \frac{x}{H^{-1}(x)} B(x)=lnH1(x)x,如果令 G ( x ) = ln ⁡ H ( x ) x G(x)=\ln\frac{H(x)}{x} G(x)=lnxH(x) 那么 G ( H − 1 ( x ) ) = B ( x ) G(H^{-1}(x))=B(x) G(H1(x))=B(x)
    拉个朗日反演:
    [ x n ] B ( x ) = [ x n ] G ( H − 1 ( x ) ) = [ x − 1 ] 1 n G ′ ( x ) 1 H ( x ) n [x^n]B(x)=[x^n]G(H^{-1}(x))=[x^{-1}]\frac{1}{n}G'(x)\frac{1}{H(x)^n} [xn]B(x)=[xn]G(H1(x))=[x1]n1G(x)H(x)n1
    那么我们可以在 S log ⁡ ( S ) S\log(S) Slog(S) 的时间求出 F ( x ) = ∑ k + 1 ∈ S b k k ! x k F(x)=\sum_{k+1\in S}\frac{b_k}{k!}x^{k} F(x)=k+1Sk!bkxk
    再令 F ( x ) F(x) F(x) 为答案的 E G F EGF EGF,按同样的方法可以得到
    F ( x ) = x exp ⁡ B ( F ( x ) ) F ( x ) exp ⁡ B ( F ( x ) ) = x F(x)=x\exp B(F(x))\\ \frac{F(x)}{\exp B(F(x))}=x F(x)=xexpB(F(x))expB(F(x))F(x)=x
    构造 F − 1 ( x ) = x exp ⁡ B ( x ) F^{-1}(x)=\frac{x}{\exp B(x)} F1(x)=expB(x)x,那么
    [ x n ] F ( x ) = [ x − 1 ] 1 n 1 ( F − 1 ( x ) ) n = [ x n − 1 ] 1 n exp ⁡ ( n B ( x ) ) [x^n]F(x)=[x^{-1}]\frac{1}{n}\frac{1}{(F^{-1}(x))^n}=[x^{n-1}]\frac{1}{n}\exp (nB(x)) [xn]F(x)=[x1]n1(F1(x))n1=[xn1]n1exp(nB(x))
#include<bits/stdc++.h>
#define cs const
#define poly vector<int>
#define pb push_back
using namespace std;
cs int N = 1e5+50, M = 1<<18|5;
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; }
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; }
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 iv[M], fac[N], ifac[N];
cs int C=18; 
poly w[C+1];
poly F; 
poly H; // 无向连通图 with root  
poly G; // 构造函数  
poly T; // cooperate with G 
poly A; // 点双内部连边个数 
int n, m;
int up, bit; poly rev;
void pre_work(int n){
	fac[0]=fac[1]=ifac[0]=ifac[1]=1;
	for(int i=2; i<=n; i++) 
	fac[i]=mul(fac[i-1],i), ifac[i]=mul(ifac[i-1],iv[i]);
}
void NTT_init(){
	for(int i=1; i<=C; i++) w[i].resize(1<<(i-1));
	int wn=ksm(3,(Mod-1)/(1<<C)); w[C][0]=1;
	for(int i=1; i<(1<<(C-1)); i++) w[C][i]=mul(w[C][i-1],wn);
	for(int i=C-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<<C); i++) iv[i]=mul(Mod-Mod/i,iv[Mod%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; 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=4;lim<(deg<<2);lim<<=1){
		c.resize(lim>>1); init(lim);
		for(int i=0; i<(lim>>1); i++) c[i]=i<(int)a.size()?a[i]:0;
		c.resize(up); b.resize(up); NTT(c); NTT(b);
		for(int i=0; i<lim; i++) Mul(b[i],dec(2,mul(b[i],c[i])));
		NTT(b,-1); b.resize(lim>>1);
	} b.resize(deg); return b;
}
poly deriv(poly a){
	for(int i=0; i+1<(int)a.size(); i++) a[i]=mul(i+1,a[i+1]);
	a.pop_back(); return a;
}
poly integ(poly a){
	a.pb(0);
	for(int i=a.size()-1;i;i--) a[i]=mul(iv[i],a[i-1]);
	a[0]=0; return a;
}
poly ln(poly a, int deg=-1){
	if(deg==-1) deg=a.size();
	a=integ(deriv(a)*inv(a,deg)); a.resize(deg); return a;
}
poly Exp(poly a, int deg=-1){
	if(deg==-1) deg = a.size(); poly b(1,1), c;
	for(int lim=2; lim<(deg<<1); lim<<=1){
		c=ln(b,lim); Dec(c[0],1);
		for(int i=0; i<lim; i++) c[i]=dec(i<(int)a.size()?a[i]:0,c[i]);
		b=b*c; b.resize(lim);
	} b.resize(deg); return b;
}
void work_F(){
	F.resize(n+2); 
	for(int i=0; i<(int)F.size(); i++) 
	F[i]=mul(ifac[i],ksm(2,(1ll*i*(i-1)/2)%(Mod-1)));
	H=ln(F); for(int i=0; i<(int)H.size(); i++) Mul(H[i],i);
	G=H; for(int i=1; i<(int)G.size(); i++) G[i-1]=G[i];
	T=G; G=deriv(ln(G)); T=ln(T);
}
int work(int x){
	poly a(x,0), b(x,0);
	for(int i=0; i<x; i++) a[i]=G[i], b[i]=T[i];
	for(int i=0; i<(int)b.size(); i++) Mul(b[i],Mod-x);
	b=a*Exp(b); return mul(iv[x],b[x-1]);
}
int work_ans(){
	for(int i=0; i<(int)A.size(); i++) Mul(A[i],n);
	A=Exp(A); return mul(A[n-1],mul(fac[n-1],iv[n]));
}
int main(){
	cin>>n>>m; 
	NTT_init(); pre_work(n+5);
	work_F(); A.resize(n+1);
	for(int i=1,x; i<=m; i++)
		scanf("%d",&x), --x, A[x]=work(x);
	cout << work_ans(); return 0;
}
  • 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、付费专栏及课程。

余额充值