[WC2019] 数树

传送门

综合性非常强的一道题。。。

给定两棵树

发现题目其实是 \(y\) 的两颗树的边集交集形成的森林的连通块数次方,map 搞一搞就行。

给定一棵树

考虑枚举两个边集的交集,根据森林的连通块数可表达为点减边,可知答案为 \(\sum_{i=0}^{n-1}g_iy^{n-i}\),其中 \(g_i\) 表示交集边数为 \(i\)第二棵树树的数量,发现 \(g_i\) 不好直接计算,是因为我们无法保证这 \(i\) 条边之外的边不在交集中,所以我们只能算出至少,再通过别的方法得到恰好。这里有两种解决方法:

第一种:考虑 \(f_i\) 表示钦定 \(i\) 条边属于交集之后第二棵树的数量,显然有:

\[f_i=\sum_{j=i}^{n-1}\binom{j}{i}g_j \]

根据二项式反演得:

\[g_i=\sum_{j=i}^{n-1}(-1)^{j-i}\binom{j}{i}f_j \]

那么答案就是:

\[\begin{aligned} Ans&=\sum_{i=0}^{n-1}y^{n-i}\sum_{j=i}^{n-1}(-1)^{j-i}\binom{j}{i}f_j\\ &=y^n\sum_{j=0}^{n-1}f_j\sum_{i=0}^j\binom{j}{i}(-1)^{j-i}(y^{-1})^i\\ &=y^n\sum_{j=0}^{n-1}f_j(y^{-1}-1)^j \end{aligned} \]

第二种:上面的式子无法使用普通的容斥,是因为有一个 \(y^{n-i}\) 在。我们首先简单化一化式子:

\[\sum_{i=0}^{n-1}g_iy^{n-i}=y^n\sum_{i=0}^{n-1}g_i(y^{-1})^i \]

考虑 \((y^{-1})^i=\sum\binom{i}{j}(y^{-1}-1)^j\),我们直接去算至少,一个钦定了 \(j\) 条边的方案我们让它算 \((y^{-1}-1)^j\) 的贡献,最后再乘回 \(y^n\) 就是正确答案了。

\[Ans=y^n\sum_{j=0}^{n-1}f_j(y^{-1}-1)^j \]

其实两种方法最后的式子都是一样的。

考虑计算 \(f_i\),对于一种选边的方案,最后可生成的树的数量可以由 prufer 序列得到,即 \(n^{n-i-2}\prod sze_j=n^{n-2}\cdot n^{-i}\prod sze_i\),其实现在已经可以树形 DP 了,我们只需要对于选边的方案进行 DP,每选一条边额外乘上 \(n^{-1}(y^{-1}-1)\) 就可以了。对于 \(\prod sze_j\),朴素的想法是记一下现在点所在的连通块大小,但是 \(\prod sze_j\) 相当于在每个连通块选一个点的方案,所以我们只需用记 \(f_{u,0/1}\) 表示做到 \(u\),目前连通块是否选了的答案,时间复杂度 \(\mathcal{O}(n)\)

不给定树

首先还是枚举交集,然后像上面那样处理成至少,答案就是:

\[Ans=y^n\sum_{j=0}^{n-1}f_j(y^{-1}-1)^j \]

注意:这里 \(f_i\) 的定义变成了钦定 \(i\) 条边后,两棵树的方案数。

这里没有树形态的限制,考虑直接写出 \(f_i\) 的通项:

\[f_s=\sum_{\sum a_i=n}\frac{n!}{(n-s)!}\prod_{i=1}^{n-s}\frac{a_i^{a_i-2}}{a_i!}(n^{n-s-2}\prod_{i=1}^{n-s} a_i)^2 \]

解释一下,一开始先枚举每个连通块大小,\(n!\) 把点放进去,但是连通块之间无序,所以除以 \((n-s)!\)\(a_i^{a_i-2}\) 是连通块内树的方案,同时连通块内点无序,除以 \(a_i!\),最后是剩下的生成两棵树的方案。

我们把这个带回去,设 \(C=(y^{-1}-1)\)

\[\begin{aligned} Ans&=y^n\sum_{s=0}^{n-1}C^s\sum_{\sum a_i=n}\frac{n!}{(n-s)!}\prod_{i=1}^{n-s}\frac{a_i^{a_i-2}}{a_i!}(n^{n-s-2}\prod_{i=1}^{n-s} a_i)^2\\ &=\frac{y^nn!}{n^4}\sum_{s=0}^{n-1}\frac{C^sn^{2n-2s}}{(n-s)!}\sum_{\sum a_i=n}\frac{a_i^{a_i}}{a_i!}\\ &=\frac{y^nn!}{n^4}\sum_{s=0}^{n-1}\frac{C^{n-s}n^{2s}}{s!}\sum_{\sum a_i=n}\frac{a_i^{a_i}}{a_i!}\\ \end{aligned} \]

既然有 \(\sum a_i=n\),不妨把后面的东西写成卷积的形式。

\[\begin{aligned} Ans&=\frac{y^nn!}{n^4}\sum_{s=0}^{n-1}\frac{C^{n-s}n^{2s}}{s!}[x^n](\sum_{j\ge1}\frac{j^j}{j!}x^j)^s\\ &=\frac{y^nn!C^n}{n^4}[x^n]\sum_{s=0}^{n-1}\frac{(\sum_{j\ge1}\frac{n^2j^j}{Cj!}x^j)^s}{s!}\\ &=\frac{y^nn!C^n}{n^4}[x^n]e^{\sum_{j\ge1}\frac{n^2j^j}{Cj!}x^j} \end{aligned} \]

所以只需要做一遍多项式 EXP 就可以解决此题啦。

#include<iostream>
#include<stdio.h>
#include<ctype.h>
#include<map>
#include<vector>
#define N 100005
#define M 530000
#define mo 998244353
#define int long long
using namespace std;
inline int read(){
	int x=0,f=0; char ch=getchar();
	while(!isdigit(ch)) f|=(ch==45),ch=getchar();
	while(isdigit(ch)) x=(x<<3)+(x<<1)+(ch^48),ch=getchar();
	return f?-x:x;
}
inline int qpow(int x,int b){
	int res=1;
	for(;b;x=x*x%mo,b>>=1) if(b&1) res=res*x%mo;
	return res;
}
int n,y,op;
namespace sub0{
	int fa[N];
	map<pair<int,int>,int> MP;
	int find(int x){
		return x==fa[x]?x:(fa[x]=find(fa[x]));
	}
	inline void merge(int x,int y){
		x=find(x),y=find(y);
		if(x==y) return;
		fa[x]=y;
	}
	void main(){
		for(int i=1;i<=n;++i) fa[i]=i;
		for(int i=1;i<n;++i){
			int x=read(),y=read();
			if(x>y) swap(x,y);
			MP[{x,y}]++;
		}
		for(int i=1;i<n;++i){
			int x=read(),y=read();
			if(x>y) swap(x,y);
			if(MP[{x,y}]) merge(x,y);
		}
		int ans=1;
		for(int i=1;i<=n;++i){
			if(i==find(i)) ans=ans*y%mo;
		}
		printf("%lld\n",ans);
	}
}
namespace sub1{
	int f[N][2],C;
	vector<int> G[N];
	void dfs(int u,int fa){
		f[u][0]=f[u][1]=1;
		for(int v:G[u]){
			if(v==fa) continue;
			dfs(v,u);
			int tmp0=f[u][0],tmp1=f[u][1];
			f[u][0]=(f[v][1]*tmp0%mo+tmp0*f[v][0]%mo*C%mo)%mo;
			f[u][1]=(f[v][1]*tmp1%mo+tmp0*f[v][1]%mo*C%mo+tmp1*f[v][0]%mo*C%mo)%mo;
		}
	}
	void main(){
		for(int i=1;i<n;++i){
			int x=read(),y=read();
			G[x].push_back(y);
			G[y].push_back(x);
		}
		C=(qpow(y,mo-2)-1)*qpow(n,mo-2)%mo;
		dfs(1,0);
		printf("%lld",f[1][1]*qpow(y,n)%mo*qpow(n,n-2)%mo);
	}
}
namespace sub2{
	inline void clear(int *A,int x,int y){for(int i=x;i<y;++i) A[i]=0;}
	inline void copy(int *A,int *B,int x,int y){for(int i=x;i<y;++i) A[i]=B[i];}
	inline void mul(int *A,int *B,int x,int y){for(int i=x;i<y;++i) A[i]=A[i]*B[i]%mo;}
	int cir[M],rt;
	inline void NTT(int *f,int len,int tag){
		if(len!=rt) for(int i=0;i<len;++i) cir[i]=(cir[i>>1]>>1)|((i&1)?len>>1:0);
		rt=len;
		for(int i=0;i<len;++i) if(i<cir[i]) swap(f[i],f[cir[i]]);
		for(int l=2;l<=len;l<<=1){
			int sze=l>>1,buf=qpow(tag?3:332748118,(mo-1)/l);
			for(int i=0;i<len;i+=l){
				int bas=1;
				for(int j=i;j<i+sze;++j,bas=bas*buf%mo){
					int tmp=bas*f[j+sze]%mo;
					f[j+sze]=(f[j]-tmp+mo)%mo,(f[j]+=tmp)%=mo;
				}
			}
		}
		if(tag==1) return;
		int invn=qpow(len,mo-2);
		for(int i=0;i<len;++i) f[i]=f[i]*invn%mo;
	}
	int tmul[M];
	inline void polyMul(int *A,int *B,int len1,int len2){
		#define sav tmul
		int len=1;
		while(len<(len1<<1)) len<<=1;
		copy(sav,B,0,len);
		NTT(A,len,1),NTT(sav,len,1),mul(A,sav,0,len),NTT(A,len,0);
		clear(A,len2,len),clear(sav,0,len);
		#undef sav
	}
	int inv1[M],inv2[M],inv3[M];
	inline void polyInv(int *A,int len){
		#define sav1 inv1
		#define sav2 inv2
		#define sav3 inv3
		int lim=1;while(lim<len) lim<<=1;
		sav1[0]=qpow(A[0],mo-2);
		for(int l=2;l<=lim;l<<=1){
			int r=l<<1;copy(sav3,A,0,l);
			for(int i=0;i<(l>>1);++i) sav2[i]=(sav1[i]<<1)%mo;
			NTT(sav1,r,1),mul(sav1,sav1,0,r);
			NTT(sav3,r,1),mul(sav1,sav3,0,r);
			NTT(sav1,r,0),clear(sav1,l,r);
			for(int i=0;i<l;++i) sav1[i]=(sav2[i]-sav1[i]+mo)%mo;
		}
		copy(A,sav1,0,len);
		clear(sav1,0,lim<<1),clear(sav2,0,lim<<1),clear(sav3,0,lim<<1);
		#undef sav1
		#undef sav2
		#undef sav3
	}
	inline void polyDer(int *A,int len){
		for(int i=1;i<len;++i) A[i-1]=A[i]*i%mo;A[len-1]=0;
	}
	inline void polyInt(int *A,int len){
		for(int i=len-1;i>=1;--i) A[i]=A[i-1]*qpow(i,mo-2)%mo;A[0]=0;
	}
	int ln1[M];
	inline void polyLn(int *A,int len){
		#define sav ln1
		copy(sav,A,0,len),polyDer(A,len),polyInv(sav,len);
		polyMul(A,sav,len,len),polyInt(A,len),clear(sav,0,len);
		#undef sav
	}
	int exp1[M],exp2[M];
	inline void polyExp(int *A,int len){
		#define sav1 exp1
		#define sav2 exp2
		int lim=1;while(lim<len) lim<<=1;
		sav1[0]=1;
		for(int l=2;l<=lim;l<<=1){
			copy(sav2,sav1,0,l>>1),polyLn(sav2,l);
			for(int i=0;i<l;++i) sav2[i]=(A[i]-sav2[i]+mo)%mo;
			sav2[0]=(sav2[0]+1)%mo;
			polyMul(sav1,sav2,l,l);
		}
		copy(A,sav1,0,len),clear(sav1,0,lim),clear(sav2,0,lim);
		#undef sav1
		#undef sav2
	}
	int fac[N],ifac[N],F[M];
	void main(){
		if(y==1) return (void)(printf("%lld",qpow(n,2*(n-2))));
		int ny=qpow(y,mo-2)-1,iny=qpow(ny,mo-2);
		fac[0]=1;for(int i=1;i<=n;++i) fac[i]=fac[i-1]*i%mo;
		ifac[n]=qpow(fac[n],mo-2);
		for(int i=n;i>=1;--i) ifac[i-1]=ifac[i]*i%mo;
		for(int i=1;i<=n;++i) F[i]=n*n%mo*iny%mo*qpow(i,i)%mo*ifac[i]%mo;
		polyExp(F,n+1);
		printf("%lld\n",fac[n]*qpow(ny,n)%mo*qpow(n,mo-5)%mo*F[n]%mo*qpow(y,n)%mo);
	}
}
signed main(){
	n=read(),y=read(),op=read();
	if(op==0) sub0::main();
	if(op==1) sub1::main();
	if(op==2) sub2::main();
	return 0;
}

posted @ 2022-06-13 09:37  CHiSwsz  阅读(48)  评论(0编辑  收藏  举报