「WC2019」数树(生成函数)(树形DP)(容斥)(多项式exp)

L O J LOJ LOJ 传送门

  • 转换一下题意得到:对于两棵树 T 1 , T 2 T1,T2 T1,T2,边集是 E 1 , E 2 E1,E2 E1,E2,那么贡献就是 y n − ∣ E 1 ∩ E 2 ∣ y^{n-|E1\cap E2|} ynE1E2

  • 第一问:直接求出并即可

  • 第二问 T 1 T1 T1 确定,不妨来枚举 T 2 T2 T2
    F ( S ) = ∑ T 2 [ T 1 ∩ T 2 = S ] F(S)=\sum_{T2} [T1\cap T2=S] F(S)=T2[T1T2=S]
    这个直接求是很不好求的,但发现 G ( S ) = ∑ T 2 [ S ⊆ T 1 ∩ T 2 ] G(S)=\sum_{T2}[S\subseteq T1\cap T2] G(S)=T2[ST1T2] 较为好求
    因为我们钦定一个边集 S S S 表示 T 1 T1 T1 T 2 T2 T2 的并,剩下的边可以随便连,设 S S S 中连通块个数为 k k k,每个连通块大小为 a i a_i ai,那么任意连边连成一棵树的方案数就是 n k − 2 ∏ a i n^{k-2}\prod a_i nk2ai

  • 引理 k k k 个大小分别为 a i a_i ai 的连通块连成一棵树的方案数是 n k − 2 ∏ a i n^{k-2}\prod a_i nk2ai
    考虑枚举 k k − 2 k^{k-2} kk2 p r u f e r prufer prufer 序列,每一个序列的贡献是 ∏ i = 1 k a i d i + 1 \prod_{i=1}^{k}a_i^{d_i+1} i=1kaidi+1 d i d_i di 为在序列中的出现次数,那么 w a y s = ( ∏ i = 1 k a i ) ∗ ( ∑ p r u f e r ∏ i = 1 k − 2 a p i ) = ( ∏ i = 1 k a i ) ∗ n k − 2 ways=(\prod_{i=1}^{k}a_i)*(\sum_{prufer}\prod_{i=1}^{k-2}a_{p_i})=(\prod_{i=1}^{k}a_i)*n^{k-2} ways=(i=1kai)(pruferi=1k2api)=(i=1kai)nk2,乘法分配率,意义显然

  • 回到第二问
    暴力把答案的式子写出来(把 y n y^n yn 提出来)
    A n s = ∑ S ⊆ T 1 F ( S ) ∗ y − ∣ S ∣ = ∑ S ⊆ T 1 y − ∣ S ∣ ∑ S ⊆ T ⊆ T 1 ( − 1 ) ∣ T ∣ − ∣ S ∣ G ( T ) = ∑ T ⊆ T 1 ( − 1 ) ∣ T ∣ G ( T ) ∑ S ⊆ T ( − y ) − ∣ S ∣ = ∑ T ⊆ T 1 ( − 1 ) ∣ T ∣ G ( T ) ∑ i = 0 ∣ T ∣ ( − y ) − i ( ∣ T ∣ i ) = ∑ T ⊆ T 1 ( 1 y − 1 ) ∣ T ∣ G ( T ) = ∑ T ⊆ T 1 ( 1 y − 1 ) ∣ T ∣ n n − ∣ T ∣ − 2 ∏ i = 1 n − ∣ T ∣ a i = ( 1 y − 1 ) n n 2 ∑ T ⊆ T 1 ∏ i = 1 n − ∣ T ∣ a i ∗ n 1 y − 1 ( y ≠ 1 ) Ans=\sum_{S\subseteq T1}F(S)*y^{-|S|}\\=\sum_{S\subseteq T1}y^{-|S|}\sum_{S\subseteq T\subseteq T1}(-1)^{|T|-|S|}G(T)\\=\sum_{T\subseteq T1}(-1)^{|T|}G(T)\sum_{S\subseteq T}(-y)^{-|S|}\\ =\sum_{T\subseteq T1}(-1)^{|T|}G(T)\sum_{i=0}^{|T|}(-y)^{-i}\binom{|T|}{i}\\=\sum_{T\subseteq T1}(\frac{1}{y}-1)^{|T|}G(T)\\ =\sum_{T\subseteq T1}(\frac{1}{y}-1)^{|T|}n^{n-|T|-2}\prod_{i=1}^{n-|T|}a_i\\=\frac{(\frac{1}{y}-1)^n}{n^2}\sum_{T\subseteq T1}\prod_{i=1}^{n-|T|}\frac{a_i*n}{\frac{1}{y}-1}(y\neq 1) Ans=ST1F(S)yS=ST1ySSTT1(1)TSG(T)=TT1(1)TG(T)ST(y)S=TT1(1)TG(T)i=0T(y)i(iT)=TT1(y11)TG(T)=TT1(y11)TnnT2i=1nTai=n2(y11)nTT1i=1nTy11ain(y=1)
    考虑后面这个式子的组合意义,就是在 T 1 T1 T1 中选择若干个连通块,每个连通块的贡献是 k ∗ a i k*a_i kai,整个的贡献是所有连通块贡献之积,考虑树形 d p dp dp f u , i f_{u,i} fu,i 表示到 u u u 当前连通块大小为 i i i 的所有方案的贡献之和,那么 w a y s = k ∑ i f 1 , i ways=k\sum if_{1,i} ways=kif1,i,暴力上生成函数, w a y s = k f ′ ( 1 ) ways=kf'(1) ways=kf(1),并且有转移
    f u ( x ) = f u ( x ) f v ( x ) + k f v ′ ( 1 ) ∗ f u ( x ) ⇒ k f u ′ ( x ) = k f ′ u ( x ) f v ( x ) + k f v ′ ( x ) f u ( x ) + k f v ′ ( 1 ) k f u ′ ( x ) f_u(x)=f_u(x)f_v(x)+kf'_v(1)*f_u(x)\\ \Rightarrow kf'_u(x)=kf'u(x)f_v(x)+kf'_v(x)f_u(x)+kf_v'(1)kf'_u(x) fu(x)=fu(x)fv(x)+kfv(1)fu(x)kfu(x)=kfu(x)fv(x)+kfv(x)fu(x)+kfv(1)kfu(x)
    维护 f u ( 1 ) , k f u ′ ( 1 ) f_u(1),kf_u'(1) fu(1),kfu(1) 就可以快速转移

  • 第三问
    同样,我们令 F ( S ) = ∑ T 1 , T 2 [ T 1 ∩ T 2 = S ] , G ( S ) = ∑ T 1 , T 2 [ S ⊆ T 1 ∩ T 2 ] F(S)=\sum_{T1,T2}[T1\cap T2=S],G(S)=\sum_{T1,T2}[S\subseteq T1\cap T2] F(S)=T1,T2[T1T2=S],G(S)=T1,T2[ST1T2]
    那么 G ( S ) G(S) G(S) 有快速计算的方法: G ( S ) = ( n k − 2 ∏ a i ) 2 G(S)=(n^{k-2}\prod a_i)^2 G(S)=(nk2ai)2
    A n s = ∑ T ( 1 y − 1 ) ∣ T ∣ G ( T ) = ∑ T ( 1 y − 1 ) ∣ T ∣ ( n n − ∣ T ∣ − 2 ∏ i = 1 n − ∣ T ∣ a i ) 2 = ( 1 y − 1 ) n n 4 ∑ T ∏ i = 1 n − ∣ T ∣ ( a i ∗ n ) 2 1 y − 1 ( y ≠ 1 ) Ans=\sum_{T}(\frac{1}{y}-1)^{|T|}G(T)\\ =\sum_{T}(\frac{1}{y}-1)^{|T|}(n^{n-|T|-2}\prod_{i=1}^{n-|T|}a_i)^2\\=\frac{(\frac{1}{y}-1)^n}{n^4}\sum_{T}\prod_{i=1}^{n-|T|}\frac{(a_i*n)^2}{\frac{1}{y}-1}(y\neq 1) Ans=T(y11)TG(T)=T(y11)T(nnT2i=1nTai)2=n4(y11)nTi=1nTy11(ain)2(y=1)
    枚举 T T T 的本质是在枚举森林,那么我们搞出每棵树的 E G F EGF EGF e x p exp exp
    F ( x ) = ∑ i = 1 i i − 2 ∗ i 2 k ∗ x i i ! F(x)=\sum_{i=1}i^{i-2}*i^2k*\frac{x^i}{i!} F(x)=i=1ii2i2ki!xi,多项式全家桶!完结撒花!

#include<bits/stdc++.h>
#define cs const
#define pb push_back
#define poly vector<int>
using namespace std;
typedef pair<int,int> pi;
typedef long long ll;
cs int Mod = 998244353;
int add(int a, int b){ return a + b >= Mod ? a + b - Mod : a + b; }
int mul(int a, int b){ return 1ll * a * b % Mod; }
int dec(int a, int b){ return a - b < 0 ? a - b + Mod : 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; }
void Add(int &a, int b){ a = add(a, b); }
void Mul(int &a, int b){ a = mul(a, b); }
void Dec(int &a, int b){ a = dec(a, b); }
int inv(int a){ return ksm(a,Mod-2); }
int sgn(int a){ return a & 1 ? Mod - 1 : 1; }
int sqr(int a){ return mul(a, a); }
cs int N = 1e5 + 50, K = 18, M = 1 << K | 5;
int n, y, op, pw[M], Coef;
int fac[M], ifac[M], iv[M];
void fac_init(){
	fac[0]=fac[1]=ifac[0]=ifac[1]=iv[0]=iv[1]=1;
	for(int i=2; i<=(1<<K); i++) fac[i]=mul(fac[i-1],i);
	for(int i=2; i<=(1<<K); i++) iv[i]=mul(Mod-Mod/i,iv[Mod%i]);
	for(int i=2; i<=(1<<K); i++) ifac[i]=mul(ifac[i-1], iv[i]);
	pw[0]=1; for(int i=1; i<=n; i++) pw[i]=mul(pw[i-1],y);
}
namespace Subtask0{
	map<pi, bool> mp;
	void work(){
		for(int i=1; i<n; i++){
			int x, y; scanf("%d%d",&x,&y);
			mp[pi(x,y)]=true; mp[pi(y,x)]=true;
		}
		int c = 0;
		for(int i=1; i<n; i++){
			int x, y; scanf("%d%d",&x,&y);
			if(mp.count(pi(x,y))) ++c;
		} cout << pw[n-c]; 
	}
}
namespace Subtask1{
	vector<int> G[N];
	int f[N], g[N], k;
	void dp(int u, int fa){
		f[u]=1; g[u]=k;
		for(int v:G[u]) if(v^fa){
			dp(v,u); int coe=add(g[v],f[v]); Mul(g[u],coe); 
			Add(g[u],mul(f[u],g[v])); Mul(f[u],coe);	
		}
	}
	void work(){
		for(int i=1; i<n; i++){
			int x, y; scanf("%d%d",&x,&y); 
			G[x].pb(y); G[y].pb(x);
		} k = mul(n,inv(dec(inv(y),1))); dp(1,0);
		cout<<mul(mul(Coef,g[1]),mul(ksm(dec(inv(y),1),n),sqr(inv(n))));
	}
}
namespace Subtask2{
	poly w[K+1], rev; int up, bit;
//	void output(poly a){ for(int i=0; i<a.size(); i++) cout<<a[i]<<" "; puts(""); }
	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];
	}
	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 f, int lim){
		poly b(1,::inv(f[0])),c;
		for(int deg=4; deg<(lim<<2); deg<<=1){
			c.resize(deg>>1);
			for(int i=0; i<(deg>>1); i++) c[i]=i<(int)f.size()?f[i]:0; 
			init(deg); 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(deg>>1);
		} b.resize(lim); return b;
	}
	poly integ(poly f){
		f.pb(0); for(int i=f.size()-1;i;i--) 
			f[i]=mul(f[i-1],iv[i]);
		f[0]=0; return f;
	}
	poly deriv(poly f){
		for(int i=1; i<(int)f.size(); i++)
			f[i-1]=mul(i,f[i]); 
		f.pop_back(); return f;
	}
	poly ln(poly f, int deg){
		f=integ(deriv(f)*inv(f,deg)); 
		f.resize(deg); return f;
	}
	poly exp(poly f, int lim){
		poly c, b(1,1); 
		for(int deg=2; deg<(lim<<1); deg<<=1){
			c=ln(b,deg);
			for(int j=0; j<deg; j++) c[j]=dec(j<(int)f.size()?f[j]:0,c[j]);
			Add(c[0],1); b=b*c; b.resize(deg);
		} b.resize(lim); return b;
	}
	void work(){
		NTT_init(); int k=mul(sqr(n),::inv(dec(::inv(y),1)));
		poly f(n+1,0); 
		for(int i=1; i<=n; i++) f[i]=mul(mul(k,ifac[i]),ksm(i,i));
		poly g=exp(f,n+1); int coe=mul(fac[n],g[n]);
		cout<<mul(mul(Coef,coe),mul(ksm(dec(::inv(y),1),n),::inv(sqr(sqr(n)))));
	}
}
int main(){
	scanf("%d%d%d",&n,&y,&op); 
	if(y==1){ cout<<ksm(n,(n-2)*op); return 0; }
	fac_init(); Coef=pw[n];
	if(op==0){ Subtask0::work(); return 0; }
	if(op==1){ Subtask1::work(); return 0; }
	if(op==2){ Subtask2::work(); return 0; }
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

FSYo

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

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

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

打赏作者

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

抵扣说明:

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

余额充值