【WC2019】数树(prufer序列,树上连通块DP,多项式exp)

设两棵树的边集分别为 E 1 , E 2 E_1,E_2 E1,E2,那么两棵树不同当且仅当它们对应的边集不同。

转化一下可以发现,染色方案等于 y n − ∣ E 1 ∩ E 2 ∣ y^{n-|E_1\cap E_2|} ynE1E2,即由边集 E 1 ∩ E 2 E_1\cap E_2 E1E2 构成的图的连通块数量。

k = 1 k=1 k=1

a n s = ∑ E 2 y n − ∣ E 1 ∩ E 2 ∣ ans=\sum_{E_2}y^{n-|E_1\cap E_2|} ans=E2ynE1E2

考虑枚举 S = E 1 ∩ E 2 S=E_1\cap E_2 S=E1E2,那么我们需要知道有多少种不同的 E 2 E_2 E2

先不考虑算重的问题,我们考虑在 S S S 的基础上添加若干边使得图构成一棵树的方案数。设边集 S S S 构成了若干个大小分别为 s 1 , s 2 , ⋯   , s k s_1,s_2,\cdots,s_k s1,s2,,sk 的连通块(显然它们都是树),根据扩展 Cayley 定理可知方案数为 n k − 2 ∏ i = 1 k s i n^{k-2}\prod_{i=1}^k s_i nk2i=1ksi,而且发现 k = n − ∣ S ∣ k=n-|S| k=nS

这样显然会算重,需要容斥,大胆假设容斥系数为 f ( ∣ S ∣ ) f(|S|) f(S)
a n s = ∑ S ⊆ E 1 f ( ∣ S ∣ ) n n − ∣ S ∣ − 2 ∏ i = 1 n − ∣ S ∣ s i ans=\sum_{S\subseteq E_1} f(|S|)n^{n-|S|-2}\prod_{i=1}^{n-|S|}s_i ans=SE1f(S)nnS2i=1nSsi
考虑一种 E 2 E_2 E2 在原式和容斥式中的贡献:(令 S = E 1 ∩ E 2 S=E_1\cap E_2 S=E1E2 y ′ = y − 1 y'=y^{-1} y=y1
y n − ∣ S ∣ = ∑ T ⊆ S f ( ∣ T ∣ ) y ′ ∣ S ∣ = ∑ ∣ T ∣ = 0 ∣ S ∣ ( ∣ S ∣ ∣ T ∣ ) f ( ∣ T ∣ ) y n \begin{aligned} y^{n-|S|}&=\sum_{T\subseteq S}f(|T|)\\ y'^{|S|}&=\sum_{|T|=0}^{|S|}\binom{|S|}{|T|}\dfrac{f(|T|)}{y^n} \end{aligned} ynSyS=TSf(T)=T=0S(TS)ynf(T)
f ( ∣ T ∣ ) y n = ( y ′ − 1 ) ∣ T ∣ \dfrac{f(|T|)}{y^n}=(y'-1)^{|T|} ynf(T)=(y1)T 即可,解得 f ( ∣ T ∣ ) = ( 1 − y ) ∣ T ∣ y n − ∣ T ∣ f(|T|)=(1-y)^{|T|}y^{n-|T|} f(T)=(1y)TynT

于是:
a n s = ∑ S ⊆ E 1 ( 1 − y ) ∣ S ∣ y n − ∣ S ∣ n n − ∣ S ∣ − 2 ∏ i = 1 n − ∣ S ∣ s i = y n n n − 2 ∑ S ⊆ E 1 ( 1 − y ) ∣ S ∣ y − ∣ S ∣ n − ∣ S ∣ ∏ i = 1 n − ∣ S ∣ s i \begin{aligned} ans&=\sum_{S\subseteq E_1}(1-y)^{|S|}y^{n-|S|}n^{n-|S|-2}\prod_{i=1}^{n-|S|}s_i\\ &=y^nn^{n-2}\sum_{S\subseteq E_1}(1-y)^{|S|}y^{-|S|}n^{-|S|}\prod_{i=1}^{n-|S|}s_i\\ \end{aligned} ans=SE1(1y)SynSnnS2i=1nSsi=ynnn2SE1(1y)SySnSi=1nSsi
这是树上连通块 DP 问题,可以设 f u , s f_{u,s} fu,s 表示考虑完 u u u 子树内的连通块情况,其中以 u u u 为根的连通块大小为 s s s,所有方案的贡献和。直接转移是 O ( n 2 ) O(n^2) O(n2) 的。

神奇的是我们可以将 ∏ i = 1 n − ∣ S ∣ s i \prod\limits_{i=1}^{n-|S|}s_i i=1nSsi 看成其组合意义:每个连通块内各选一个点的方案数。于是我们可以设 f u , 0 / 1 f_{u,0/1} fu,0/1 表示考虑完 u u u 子树内的连通块情况,其中以 u u u 为根的连通块是否已经选了点,所有方案的贡献和(此时的两种方案不同当且仅当连通块情况不同或连通块内选点情况不同)。那么转移就是 O ( n ) O(n) O(n) 的了。

k = 2 k=2 k=2

a n s = ∑ E 1 ∑ E 2 y n − ∣ E 1 ∩ E 2 ∣ ans=\sum_{E_1}\sum_{E_2}y^{n-|E_1\cap E_2|} ans=E1E2ynE1E2

同样地考虑枚举 S = E 1 ∩ E 2 S=E_1\cap E_2 S=E1E2,设 g ( S ) g(S) g(S) 为在 S S S 的基础上添加若干边使得图构成一棵树的方案数, k = 1 k=1 k=1 时已经算过了为 g ( S ) = n n − ∣ S ∣ − 2 ∏ i = 1 n − ∣ S ∣ s i g(S)=n^{n-|S|-2}\prod\limits_{i=1}^{n-|S|}s_i g(S)=nnS2i=1nSsi,其中边集 S S S 构成了若干个大小分别为 s 1 , s 2 , ⋯   , s n − ∣ S ∣ s_1,s_2,\cdots,s_{n-|S|} s1,s2,,snS 的连通块。

同样大胆假设容斥系数 f ( ∣ S ∣ ) f(|S|) f(S)
a n s = ∑ S f ( ∣ S ∣ ) g ( S ) 2 ans=\sum_{S}f(|S|)g(S)^2 ans=Sf(S)g(S)2
考虑一种 E 1 , E 2 E_1,E_2 E1,E2 在原式和容斥式中的贡献:(设 S = E 1 ∩ E 2 S=E_1\cap E_2 S=E1E2
y n − ∣ S ∣ = ∑ T ⊆ S f ( ∣ T ∣ ) y^{n-|S|}=\sum_{T\subseteq S}f(|T|) ynS=TSf(T)
同样可以得到 f ( ∣ T ∣ ) = ( 1 − y ) ∣ T ∣ y n − ∣ T ∣ f(|T|)=(1-y)^{|T|}y^{n-|T|} f(T)=(1y)TynT。于是:
a n s = ∑ S ( 1 − y ) ∣ S ∣ y n − ∣ S ∣ g ( S ) 2 = y n n 2 n − 4 ∑ S ( 1 − y ) ∣ S ∣ y − ∣ S ∣ n − 2 ∣ S ∣ ∏ i = 1 n − ∣ S ∣ s i 2 \begin{aligned} ans&=\sum_{S}(1-y)^{|S|}y^{n-|S|}g(S)^2\\ &=y^nn^{2n-4}\sum_{S}(1-y)^{|S|}y^{-|S|}n^{-2|S|}\prod\limits_{i=1}^{n-|S|}s_i^2 \end{aligned} ans=S(1y)SynSg(S)2=ynn2n4S(1y)SySn2Si=1nSsi2
S S S 不太好枚举,不如直接转为枚举 s i s_i si,即把 { 1 , ⋯   , n } \{1,\cdots,n\} {1,,n} 分为若干个集合,每个大小为 s s s 的集合为一个连通块(且是树),有 s s − 2 s^{s-2} ss2 种生成树方案,每种方案的贡献都是 ( 1 − y ) s − 1 y − ( s − 1 ) n − 2 ( s − 1 ) s 2 (1-y)^{s-1}y^{-(s-1)}n^{-2(s-1)}s^2 (1y)s1y(s1)n2(s1)s2,于是这个连通块总贡献为 ( 1 − y ) s − 1 y − ( s − 1 ) n − 2 ( s − 1 ) s s (1-y)^{s-1}y^{-(s-1)}n^{-2(s-1)}s^s (1y)s1y(s1)n2(s1)ss

考虑其指数生成函数:
F ( x ) = ∑ s ≥ 1 1 s ! ( 1 − y ) s − 1 y − ( s − 1 ) n − 2 ( s − 1 ) s s x s = ∑ s ≥ 0 1 s ! ( 1 − y ) s y − s n − 2 s ( s + 1 ) s x s + 1 \begin{aligned} F(x)&=\sum\limits_{s\geq 1}\frac{1}{s!}(1-y)^{s-1}y^{-(s-1)}n^{-2(s-1)}s^sx^s\\ &=\sum\limits_{s\geq 0}\frac{1}{s!}(1-y)^{s}y^{-s}n^{-2s}(s+1)^sx^{s+1}\\ \end{aligned} F(x)=s1s!1(1y)s1y(s1)n2(s1)ssxs=s0s!1(1y)sysn2s(s+1)sxs+1
于是 a n s = y n n 2 n − 4 [ x n ] n ! e F ( x ) ans=y^nn^{2n-4}[x^n]n!e^{F(x)} ans=ynn2n4[xn]n!eF(x),多项式 exp 即可,时间复杂度 O ( n log ⁡ n ) O(n\log n) O(nlogn)

#include<bits/stdc++.h>

#define N 100010

using namespace std;

namespace modular
{
	const int mod=998244353;
	inline int add(int x,int y){return x+y>=mod?x+y-mod:x+y;}
	inline int dec(int x,int y){return x-y<0?x-y+mod:x-y;}
	inline int mul(int x,int y){return 1ll*x*y%mod;}
	inline void Add(int &x,int y){x=x+y>=mod?x+y-mod:x+y;}
	inline void Dec(int &x,int y){x=x-y<0?x-y+mod:x-y;}
	inline void Mul(int &x,int y){x=1ll*x*y%mod;}
}using namespace modular;

inline int poww(int a,int b)
{
	int ans=1;
	while(b)
	{
		if(b&1) ans=mul(ans,a);
		a=mul(a,a);
		b>>=1;
	}
	return ans;
}

inline int read()
{
	int x=0,f=1;
	char ch=getchar();
	while(ch<'0'||ch>'9')
	{
		if(ch=='-') f=-1;
		ch=getchar();
	}
	while(ch>='0'&&ch<='9')
	{
		x=(x<<1)+(x<<3)+(ch^'0');
		ch=getchar();
	}
	return x*f;
}

int n,y;

namespace sub0
{
	#define pii pair<int,int>
	#define mk(a,b) make_pair(a,b)
	set<pii>e;
	void main()
	{
		for(int i=1;i<n;i++)
		{
			int u=read(),v=read();
			if(u>v) swap(u,v);
			e.insert(mk(u,v));
		}
		int same=0;
		for(int i=1;i<n;i++)
		{
			int u=read(),v=read();
			if(u>v) swap(u,v);
			if(e.find(mk(u,v))!=e.end()) same++;
		}
		printf("%d\n",poww(y,n-same));
	}
	#undef pii
	#undef mk
}

namespace sub1
{
	int invy,invn,coef;
	int cnt,head[N],nxt[N<<1],to[N<<1];
	int f[N][2];
	void adde(int u,int v)
	{
		to[++cnt]=v;
		nxt[cnt]=head[u];
		head[u]=cnt;
	}
	void dfs(int u,int fa)
	{
		static int g[2];
		f[u][0]=f[u][1]=1;
		for(int i=head[u];i;i=nxt[i])
		{
			int v=to[i];
			if(v==fa) continue;
			dfs(v,u);
			g[0]=mul(f[u][0],f[v][1]);
			Add(g[0],mul(f[u][0],mul(f[v][0],coef)));
			g[1]=mul(f[u][1],f[v][1]);
			Add(g[1],mul(f[u][0],mul(f[v][1],coef)));
			Add(g[1],mul(f[u][1],mul(f[v][0],coef)));
			f[u][0]=g[0];
			f[u][1]=g[1];
		}
	}
	void main()
	{
		invy=poww(y,mod-2),invn=poww(n,mod-2);
		coef=mul(dec(1,y),mul(invy,invn));
		for(int i=1;i<n;i++)
		{
			int u=read(),v=read();
			adde(u,v),adde(v,u);
		}
		dfs(1,0);
		printf("%d\n",mul(mul(poww(y,n),poww(n,n-2)),f[1][1]));
	}
}

namespace sub2
{
	#define LN 20
	int fac[N<<3],ifac[N<<3];
	vector<int>w[LN][2];
	void init(int limit)
	{
		for(int bit=0,mid=1;mid<limit;bit++,mid<<=1)
		{
			int len=mid<<1;
			int gn=poww(3,(mod-1)/len);
			int ign=poww(gn,mod-2);
			int g=1,ig=1;
			for(int j=0;j<mid;j++,g=mul(g,gn),ig=mul(ig,ign))
				w[bit][0].push_back(g),w[bit][1].push_back(ig);
		}
	}
	void NTT(int *a,int limit,int opt)
	{
		static int rev[N<<3];
		opt=(opt<0);
		for(int i=0;i<limit;i++)
			rev[i]=(rev[i>>1]>>1)|((i&1)*(limit>>1));
		for(int i=0;i<limit;i++)
			if(i<rev[i]) swap(a[i],a[rev[i]]);
		for(int bit=0,mid=1;mid<limit;bit++,mid<<=1)
		{
			for(int i=0,len=mid<<1;i<limit;i+=len)
			{
				for(int j=0;j<mid;j++)
				{
					int x=a[i+j],y=mul(w[bit][opt][j],a[i+mid+j]);
					a[i+j]=add(x,y),a[i+mid+j]=dec(x,y);
				}
			}
		}
		if(opt)
		{
			int tmp=poww(limit,mod-2);
			for(int i=0;i<limit;i++)
				a[i]=mul(a[i],tmp);
		}
	}
	void getinv(int *f,int *g,int n)
	{
		static int ff[N<<3];
		g[0]=poww(f[0],mod-2);
		int now=2;
		for(;now<(n<<1);now<<=1)
		{
			int limit=now<<1;
			for(int i=0;i<now;i++) ff[i]=f[i];
			NTT(ff,limit,1),NTT(g,limit,1);
			for(int i=0;i<limit;i++)
				g[i]=mul(dec(2,mul(ff[i],g[i])),g[i]);
			NTT(g,limit,-1);
			for(int i=now;i<limit;i++) g[i]=0;
		}
		for(int i=n;i<now;i++) g[i]=0;
		for(int i=0;i<now;i++) ff[i]=0;
	}
	void getder(int *f,int *g,int n)
	{
		for(int i=1;i<n;i++) g[i-1]=mul(i,f[i]);
		g[n-1]=0;
	}
	void getint(int *f,int *g,int n)
	{
		for(int i=n-1;i>=1;i--) g[i]=mul(mul(ifac[i],fac[i-1]),f[i-1]);
		g[0]=0;
	}
	void getln(int *f,int *g,int n)
	{
		static int derf[N<<3],invf[N<<3];
		getder(f,derf,n);
		getinv(f,invf,n);
		int limit=1;
		while(limit<(n<<1)) limit<<=1;
		NTT(derf,limit,1),NTT(invf,limit,1);
		for(int i=0;i<limit;i++)
			derf[i]=mul(derf[i],invf[i]);
		NTT(derf,limit,-1);
		getint(derf,g,n);
		for(int i=0;i<limit;i++) derf[i]=invf[i]=0;
	}
	void getexp(int *f,int *g,int n)
	{
		static int ff[N<<3],lng[N<<3];
		assert(!f[0]);
		g[0]=1;
		int now=2;
		for(;now<(n<<1);now<<=1)
		{
			int limit=now<<1;
			getln(g,lng,now);
			for(int i=0;i<now;i++) ff[i]=f[i];
			for(int i=0;i<now;i++) Dec(ff[i],lng[i]);
			Add(ff[0],1);
			NTT(g,limit,1),NTT(ff,limit,1);
			for(int i=0;i<limit;i++)
				g[i]=mul(g[i],ff[i]);
			NTT(g,limit,-1);
			for(int i=now;i<limit;i++) g[i]=0;
		}
		for(int i=n;i<now;i++) g[i]=0;
		for(int i=0;i<now;i++) lng[i]=ff[i]=0;
	}
	void main()
	{
		int limit=1;
		while(limit<=(n<<2)) limit<<=1;
		init(limit);
		fac[0]=1;
		for(int i=1;i<=(n<<3);i++) fac[i]=mul(fac[i-1],i);
		ifac[n<<3]=poww(fac[n<<3],mod-2);
		for(int i=(n<<3);i>=1;i--) ifac[i-1]=mul(ifac[i],i);
		static int f[N<<3],g[N<<3];
		int coef=1,tmp=mul(mul(dec(1,y),poww(y,mod-2)),poww(mul(n,n),mod-2));
		for(int i=0;i<n;i++)
		{
			f[i+1]=mul(mul(ifac[i],coef),poww(i+1,i));
			Mul(coef,tmp);
		}
		getexp(f,g,n+1);
		printf("%d\n",mul(mul(poww(y,n),poww(n,2*n-4)),mul(g[n],fac[n])));
	}
	#undef LN
}

int main()
{
	n=read(),y=read();int opt=read();
	if(!opt) sub0::main();
	else if(opt==1) sub1::main();
	else sub2::main();
	return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值