【LuoguP5206】[WC2019] 数树

题目链接

题意

定义 F ( T 1 , T 2 ) = y n − c o m m o n F(T_1,T_2)=y^{n-common} F(T1,T2)=yncommon 其中 c o m m o n common common 为两棵树 T 1 , T 2 T_1,T_2 T1,T2 的公共边条数。

三种问题
1.给定 T 1 , T 2 T_1,T_2 T1,T2
2.给定 T 1 T1 T1 T 2 T_2 T2任意
3.均任意

Sol

第一种 std::set<int> 存边即可

这道题的关键在第二问。

考虑要求的式子:
我们令 c c c 是重边条数。
a n s = ∑ T 2 y n − c = y n ∑ T 2 y − c ans=\sum_{T_2} y^{n-c}=y^n\sum_{T_2}y^{-c} ans=T2ync=ynT2yc

一个显然的想法就是枚举 c c c 具体是多少然后容斥计算。

但是这里有一个更为方便的方法。
因为限制等价是恰好有 c 条重边,然后贡献是一个 x k x^k xk 的形式,我们可以套用一下二项式定理。

a n s = y n ∑ ( y − 1 − 1 + 1 ) c a n s = y n ∑ ∑ i = 0 c ( c i ) ( y − 1 − 1 ) i ans= y^n\sum (y^{-1}-1+1)^c\\ ans= y^n\sum \sum_{i=0}^c {c\choose i}(y^{-1}-1)^i\\ ans=yn(y11+1)cans=yni=0c(ic)(y11)i

容易发现即是每一个重边集合的子集都会贡献一次 ( y − 1 − 1 ) i (y^{-1}-1)^i (y11)i 的答案,其中 i i i 代表了重边集合的大小。

这样子答案就变成了:

a n s = y n ∑ E ⊆ E T 1 ( y − 1 − 1 ) ∣ E ∣ ∗ F ( E ) ans=y^n\sum_{E\subseteq E_{T_1}} (y^{-1}-1)^{|E|}*F(E) ans=ynEET1(y11)EF(E)

其中 F ( E ) F(E) F(E) 表示的是一棵含有 E E E 这个边集的的数目。

这个大力用 p r u f e r prufer prufer序列推式子可以得出:
F ( E ) = n m − 2 ∏ i = 1 m s i z e i F(E)=n^{m-2}\prod_{i=1}^m size_i F(E)=nm2i=1msizei
m m m 是边集形成的连通块个数。

回代:
a n s = y n ∑ E ⊆ E T 1 ( y − 1 − 1 ) ∣ E ∣ n m − 2 ∏ i = 1 m s i z e i ans=y^n\sum_{E\subseteq E_{T_1}} (y^{-1}-1)^{|E|}n^{m-2}\prod_{i=1}^m size_i ans=ynEET1(y11)Enm2i=1msizei

显然我们有 ∣ E ∣ = n − m |E|=n-m E=nm
为了方便我们把所有和 m 无关的东西往外面放:

a n s = ( 1 − y ) n ∗ n − 2 ∑ E ⊆ E T 1 ( ( y − 1 − 1 ) − 1 n ) m ∏ i = 1 m s i z e i ans=(1-y)^n*n^{-2}\sum_{E\subseteq E_{T_1}} \bigg((y^{-1}-1)^{-1}n\bigg)^m\prod_{i=1}^m size_i ans=(1y)nn2EET1((y11)1n)mi=1msizei

这个东西只和连通块个数及大小有关。
看到后面带有乘法操作,那么很容易想到思考一下组合意义

可以认为就是每一个连通块里面选出一个关键点的方案数。
那么我们就是要求把树 T 1 T_1 T1 分成若干连通块并在每一个块内选取一个关键点的方案数的和,每多选出一个连通块对答案的贡献就乘上一个 系数 K = n ( y − 1 − 1 ) − 1 K=n(y^{-1}-1)^{-1} K=n(y11)1

这个就可以直接树形 d p dp dp O ( n ) O(n) O(n) 解决了,设 f [ i ] [ 0 / 1 ] f[i][0/1] f[i][0/1] 就行了。

接下来就是第三问了,这里显然会用到生成函数了。

答案的式子就是多了个枚举:
a n s = ( 1 − y ) n ∗ n − 2 ∑ T 1 ∑ E ⊆ E T 1 ( ( y − 1 − 1 ) − 1 n ) m ∏ i = 1 m s i z e i ans=(1-y)^n*n^{-2}\sum_{T_1}\sum_{E\subseteq E_{T_1}} \bigg((y^{-1}-1)^{-1}n\bigg)^m\prod_{i=1}^m size_i ans=(1y)nn2T1EET1((y11)1n)mi=1msizei

我们其实在式子中就没有用到过 T 1 T_1 T1,所以它也是个凑方案数的:

a n s = ( 1 − y ) n ∗ n − 2 ∑ E F ( E ) ( ( y − 1 − 1 ) − 1 n ) m ∏ i = 1 m s i z e i ans=(1-y)^n*n^{-2}\sum_{E} F(E)\bigg((y^{-1}-1)^{-1}n\bigg)^m\prod_{i=1}^m size_i ans=(1y)nn2EF(E)((y11)1n)mi=1msizei

乘上一个含有这种边集的树的方案数就行了,按照之前的结论:

a n s = ( 1 − y ) n ∗ n − 2 ∑ E n m − 2 ( ( y − 1 − 1 ) − 1 n ) m ∏ i = 1 m s i z e i 2 ans=(1-y)^n*n^{-2}\sum_{E} n^{m-2}\bigg((y^{-1}-1)^{-1}n\bigg)^m\prod_{i=1}^m size_i^2 ans=(1y)nn2Enm2((y11)1n)mi=1msizei2

a n s = ( 1 − y ) n ∗ n − 4 ∑ E ( n 2 ( y − 1 − 1 ) − 1 ) m ∏ i = 1 m s i z e i 2 ans=(1-y)^n*n^{-4}\sum_{E} \bigg(n^2(y^{-1}-1)^{-1}\bigg)^m\prod_{i=1}^m size_i^2 ans=(1y)nn4E(n2(y11)1)mi=1msizei2

美化一下式子,令 B = ( 1 − y ) n ∗ n − 4 , K = n 2 ( y − 1 − 1 ) − 1 B=(1-y)^n*n^{-4},K=n^2(y^{-1}-1)^{-1} B=(1y)nn4,K=n2(y11)1

a n s = B ∑ E K m ∏ i = 1 m s i z e i 2 ans=B\sum_{E} K^m\prod_{i=1}^m size_i^2 ans=BEKmi=1msizei2

这个生成函数构造很明显了:

g i = K ∗ i 2 ∗ i i − 2 = K ∗ i i g_i=K*i^2*i^{i-2}=K*i^i gi=Ki2ii2=Kii 就是 i i i 个点一个连通块时的后面那个式子的贡献(后面那个是这种树的方案数)
E G F EGF EGF : G ( x ) = ∑ i = 1 g i x i i ! G(x)=\sum_{i=1} \frac{g_ix^i}{i!} G(x)=i=1i!gixi
用指数型生成函数,卷积就是合并两个连通块了。
所以我们似乎要求:
∑ i = 1 n G ( x ) i \sum_{i=1}^n G(x)^i i=1nG(x)i

注意到我们会算重,那么应该是:
∑ i = 1 n G ( x ) i i ! \sum_{i=1}^n \frac{G(x)^i}{i!} i=1ni!G(x)i

因为连通块之间是无序的。
所以这东西就是一个 e G ( x ) e^{G(x)} eG(x) 就做完了。

多项式 e x p exp exp 即可。

code:

#include<bits/stdc++.h>
#define Set(a,b) memset(a,b,sizeof(a))
#define Copy(a,b) memcpy(a,b,sizeof(a))
#define Clear(a,_begin_,_end_) for(int i=_begin_;i<_end_;++i) a[i]=0
using namespace std;
const int N=1e5+10;
const int mod=998244353;
template <typename T> inline void init(T&x){
	x=0;char ch=getchar();bool t=0;
	for(;ch>'9'||ch<'0';ch=getchar()) if(ch=='-') t=1;
	for(;ch>='0'&&ch<='9';ch=getchar()) x=(x<<1)+(x<<3)+(ch-48);
	if(t) x=-x;return;
}
typedef long long ll;
template<typename T>inline void Inc(T&x,int y){x+=y;if(x>=mod) x-=mod;return;}
template<typename T>inline void Dec(T&x,int y){x-=y;if(x <  0) x+=mod;return;}
template<typename T>inline int fpow(int x,T k){int ret=1;for(;k;k>>=1,x=(ll)x*x%mod) if(k&1) ret=(ll)ret*x%mod;return ret;}
inline int Sum(int x,int y){x+=y;if(x>=mod) return x-mod;return x;}
inline int Dif(int x,int y){x-=y;if(x < 0 ) return x+mod;return x;}
int n,y,op;
namespace Task1{
	set<int> E[N];
	void work(){
		int u,v;
		for(int i=1;i<n;++i) {
			init(u),init(v);if(u>v) swap(u,v);
			E[u].insert(v);
		}int cnt=0;
		for(int i=1;i<n;++i) {
			init(u),init(v);if(u>v) swap(u,v);
			if(E[u].find(v)!=E[u].end()) ++cnt;
		}cout<<fpow(y,n-cnt)<<endl;
	}
}
namespace Task2{
	int f[N][2];
	struct edge{int to,next;}a[N<<1];
	int head[N],cnt=0,iy;
	inline void add(int x,int y){a[++cnt]=(edge){y,head[x]};head[x]=cnt;}
	void dfs(int u,int fa){
		f[u][0]=1,f[u][1]=1;
		for(int v,i=head[u];i;i=a[i].next) {
			v=a[i].to;if(v==fa) continue;dfs(v,u);
			f[u][1]=Sum((ll)f[u][1]*((ll)f[v][0]*iy%mod+f[v][1])%mod,(ll)f[u][0]*f[v][1]%mod*iy%mod);
			f[u][0]=(ll)f[u][0]*((ll)f[v][0]*iy%mod+f[v][1])%mod;
		}
		f[u][0]=(ll)f[u][0]*y%mod;
		f[u][1]=(ll)f[u][1]*y%mod;
		return;
	}
	void work(){
		if(y==1) return void(printf("%d\n",fpow(n,n-2)));
		for(int i=1;i<n;++i){int u,v;init(u),init(v);add(u,v),add(v,u);}
		int base=(ll)fpow(1-y+mod,n)*fpow(n,mod-3)%mod;
		y=(ll)fpow(fpow(y,mod-2)-1,mod-2)*n%mod;
		iy=fpow(y,mod-2),dfs(1,0);int ans=f[1][1];
		ans=(ll)ans*base%mod;
		printf("%d\n",ans);
		return;
	}
}
namespace Task3{
	const int MAXN=N<<2;
	int rader[MAXN];
	const int SIZE=sizeof(rader);
	int wn[30],iwn[30],Inv[MAXN];
	inline void Calcw(){for(int i=0;i<30;++i) wn[i]=fpow(3,(mod-1)/(1<<i)),iwn[i]=fpow(wn[i],mod-2);}
	inline void Calc_Inversion(){Inv[1]=1;for(int i=2;i<MAXN;++i) Inv[i]=(ll)(mod-mod/i)*Inv[mod%i]%mod;}
	inline int Init(int m){
		int len=1,up=-1;for(;len<m;len<<=1,++up);
		for(int i=0;i<len;++i) rader[i]=(rader[i>>1]>>1)|((i&1)<<up);
		return len;
	}
	inline void NTT(int*A,int n,int f){
		for(int i=0;i<n;++i) if(rader[i]>i) swap(A[rader[i]],A[i]);
		for(int i=1,h=1;i<n;i<<=1,++h){
			int W=(~f)? wn[h]:iwn[h];
			for(int j=0,p=i<<1;j<n;j+=p)
				for(int w=1,k=0;k<i;++k,w=(ll)w*W%mod) {
					int X=A[j|k],Y=(ll)A[j|k|i]*w%mod;
					A[j|k]=Sum(X,Y),A[j|k|i]=Dif(X,Y);
				}
		}if(!~f) for(int i=0;i<n;++i) A[i]=(ll)A[i]*Inv[n]%mod;
		return;
	}
	int fac[N],finv[N];
	inline void Mul(const int*a,const int*b,int*c,const int n,const int m) {
		int L=n+m-1;int len=Init(L);static int A[MAXN],B[MAXN];
		for(int i=0;i<n;++i) A[i]=a[i];for(int i=0;i<m;++i) B[i]=b[i];
		Clear(A,n,len);Clear(B,m,len);NTT(A,len,1),NTT(B,len,1);
		for(int i=0;i<len;++i) c[i]=(ll)A[i]*B[i]%mod;
		NTT(c,len,-1);return;
	}
	inline void Poly_Inv(const int*F,int*I,const int n){
		if(n==1) {memset(I,0,SIZE);I[0]=fpow(F[0],mod-2);return;}
		Poly_Inv(F,I,(n+1)>>1);int L=n<<1,len=Init(L);
		static int A[MAXN];for(int i=0;i<n;++i) A[i]=F[i];Clear(A,n,len);
		NTT(I,len,1);NTT(A,len,1);
		for(int i=0;i<len;++i) I[i]=Dif(Sum(I[i],I[i]),(ll)I[i]*I[i]%mod*A[i]%mod);
		NTT(I,len,-1);Clear(I,n,len);return;
	}
	inline void Direv(int*A,const int n){for(int i=0;i<n;++i) A[i]=(ll)A[i+1]*(i+1)%mod;A[n-1]=0;return;}
	inline void Integ(int*A,const int n){for(int i=n-1;i;--i) A[i]=(ll)A[i-1]*Inv[i]%mod; A[0]=0;return;}
	inline void Poly_Ln(const int*F,int*L,const int n) {
		static int A[MAXN],B[MAXN];for(int i=0;i<n;++i) A[i]=F[i];
		Direv(A,n);Poly_Inv(F,B,n);Mul(A,B,L,n,n);
		int TL=(n<<1)-1;Clear(L,n,TL);Integ(L,n);return;
	}
	inline void Poly_Exp(const int*F,int*E,const int n){
		if(n==1) {memset(E,0,SIZE);E[0]=1;return;} static int A[MAXN];
		Poly_Exp(F,E,(n+1)>>1);Poly_Ln(E,A,n);
		for(int i=0;i<n;++i) A[i]=Dif(F[i],A[i]);Inc(A[0],1);
		Mul(E,A,E,n,n);int TL=(n<<1)-1;Clear(E,n,TL);return;
	}
	void work(){
		if(y==1) return void(printf("%d\n",fpow(n,(n-2)<<1)));
		Calcw(),Calc_Inversion();
		int base=(ll)fpow(1-y+mod,n)*fpow(n,mod-5)%mod;
		y=(ll)fpow(fpow(y,mod-2)-1,mod-2)*n%mod*n%mod;
		fac[0]=finv[0]=1;
		for(int i=1;i<=n;++i) fac[i]=(ll)fac[i-1]*i%mod,finv[i]=(ll)finv[i-1]*Inv[i]%mod;
		static int F[MAXN];
		for(int i=1;i<=n;++i) F[i]=(ll)y*fpow(i,i)%mod*finv[i]%mod;
		static int E[MAXN];Poly_Exp(F,E,n+1);
		int ans=(ll)E[n]*fac[n]%mod;
		ans=(ll)ans*base%mod;
		printf("%d\n",ans);
		return;
	}
}
int main()
{
	init(n),init(y),init(op);
	if(op==0) Task1::work();
	else if(op==1) Task2::work();
	else if(op==2) Task3::work();
	return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值