【LOJ #2983】「WC2019」数树(容斥 / 生成函数 / 树形DP / 多项式exp)

传送门

感觉就是对着 z x y zxy zxy博客推的
不过还是记一下吧


S u b t a s k 1 Subtask1 Subtask1

显然就是求有多少条红蓝边相同
如果有 x x x条,答案就是 y n − x y^{n-x} ynx
随便爱咋做咋做

S u b t a s k 2 Subtask2 Subtask2

考虑如果我们枚举边集合 S S S
F ( s ) = ∑ T 2 [ T 1 ∩ T 2 = S ] , G ( s ) = ∑ T 1 [ S ⊆ T 1 ∩ T 2 ] F(s)=\sum_{T_2}[T_1\cap T_2=S],G(s)=\sum_{T_1}[S\subseteq T_1\cap T_2] F(s)=T2[T1T2=S],G(s)=T1[ST1T2]
那么有 F ( S ) = ∑ S ⊆ T ( − 1 ) ∣ T ∣ − ∣ S ∣ G ( T ) F(S)=\sum_{S\subseteq T}(-1)^{|T|-|S|}G(T) F(S)=ST(1)TSG(T)
考虑 a n s = ∑ S F ( S ) y n − ∣ S ∣ ans=\sum_{S}F(S)y^{n-|S|} ans=SF(S)ynS
= y n ∑ S ⊆ T 1 y − ∣ S ∣ ∑ S ⊆ T ( − 1 ) ∣ T ∣ − ∣ S ∣ G ( T ) =y^n\sum_{S\subseteq T_1}y^{-|S|}\sum_{S\subseteq T}(-1)^{|T|-|S|}G(T) =ynST1ySST(1)TSG(T)
= y n ∑ T ⊆ T 1 G ( T ) ( − 1 ) ∣ T ∣ ∑ S ⊆ T ( − y ) − ∣ S ∣ =y^n\sum_{T\subseteq T_1}G(T)(-1)^{|T|}\sum_{S\subseteq T}(-y)^{-|S|} =ynTT1G(T)(1)TST(y)S
= y n ∑ T ⊆ T 1 G ( T ) ( − 1 ) ∣ T ∣ ∑ i = 0 ∣ T ∣ ( − y ) − i ( ∣ T ∣ i ) =y^n\sum_{T\subseteq T_1}G(T)(-1)^{|T|}\sum_{i=0}^{|T|}(-y)^{-i}{|T|\choose i} =ynTT1G(T)(1)Ti=0T(y)i(iT)
= y n ∑ T ⊆ T 1 G ( T ) ( − 1 ) ∣ T ∣ ( 1 − 1 y ) ∣ T ∣ =y^n\sum_{T\subseteq T_1}G(T)(-1)^{|T|}(1-\frac 1 y)^{|T|} =ynTT1G(T)(1)T(1y1)T
= y n ∑ T ⊆ T 1 G ( T ) ( 1 y − 1 ) ∣ T ∣ =y^n\sum_{T\subseteq T_1}G(T)(\frac 1 y-1)^{|T|} =ynTT1G(T)(y11)T

考虑这时 G ( T ) G(T) G(T)的意义就是将 T T T的边连上后得到的森林的生成树个数
设形成了 k k k个连通块,每个连通块大小为 a i a_i ai
那么有总方案数位 n k − 2 ∏ a i n^{k-2}\prod_{a_i} nk2ai
证明可以考虑 p r u f e r prufer prufer序列,设序列为 p , p, p,每个点出现了 d i d_i di
∑ p ∏ i a i d i + 1 = ( ∏ i a i ) ∑ i = 1 k a i d i = ( ∏ i a i ) ∑ p ∏ i = 1 k − 2 a p i = ( ∏ i a i ) ∏ i = 1 k − 2 ∑ p a p i = ( ∏ i a i ) ∏ i = 1 k − 2 n = n k − 2 ( ∏ i a i ) \sum_{p}\prod_{i}a_i^{d_i+1}=(\prod_{i}a_i)\sum_{i=1}^ka_i^{d_i}=(\prod_{i}a_i)\sum_p\prod_{i=1}^{k-2}a_{p_i}=(\prod_{i}a_i)\prod_{i=1}^{k-2}\sum_{p}a_{p_i}=(\prod_{i}a_i)\prod_{i=1}^{k-2}n=n^{k-2}(\prod_{i}a_i) piaidi+1=(iai)i=1kaidi=(iai)pi=1k2api=(iai)i=1k2papi=(iai)i=1k2n=nk2(iai)

a n s = y n ∑ T ⊆ T 1 ( 1 y − 1 ) ∣ T ∣ n n − ∣ T ∣ − 2 ∏ i = 1 n − ∣ T ∣ a i ans=y^n\sum_{T\subseteq T_1}(\frac 1 y-1)^{|T|}n^{n-|T|-2}\prod_{i=1}^{n-|T|}a_i ans=ynTT1(y11)TnnT2i=1nTai
= y n ( 1 y − 1 ) n n 2 ∑ T ⊆ T 1 ∏ i = 1 n − ∣ T ∣ a i n 1 y − 1 =y^n\frac{(\frac 1 y-1)^n}{n^2}\sum_{T\subseteq T_1}\prod_{i=1}^{n-|T|}\frac{a_in}{\frac 1 y-1} =ynn2(y11)nTT1i=1nTy11ain
考虑实际意义则就是对于树上一个大小为 i i i的连通块贡献为 i n 1 y − 1 \frac{in}{\frac 1 y-1} y11in
k = n 1 y − 1 k=\frac n{\frac 1 y-1} k=y11n
一个划分的贡献是所有连通块的积
考虑用树形 d p dp dp
f [ u ] [ i ] f[u][i] f[u][i]表示 u u u为根, u u u所在连通块大小为 i i i的所有贡献之和
那么答案就是 k ∑ i f [ 1 ] [ i ] ∗ i k\sum_if[1][i]*i kif[1][i]i

考虑直接树形 d p dp dp O ( n 2 ) O(n^2) O(n2)
考虑利用生成函数优化
观察答案发现是求导后取 x = 1 x=1 x=1的点值
那么有 F u ( x ) = ( k F v ′ ( 1 ) + F v ( x ) ) ∗ F u ( x ) F_u(x)=(kF_v'(1)+F_v(x))*F_u(x) Fu(x)=(kFv(1)+Fv(x))Fu(x)
f u = F u ( 1 ) , g u = k F u ′ ( 1 ) f_u=F_u(1),g_u=kF_u'(1) fu=Fu(1),gu=kFu(1)
那么根据上面的式子有
g u = g u ∗ ( g v + f v ) + f u g v , f u = f u ∗ ( g v + f v ) g_u=g_u*(g_v+f_v)+f_ug_v,f_u=f_u*(g_v+f_v) gu=gu(gv+fv)+fugv,fu=fu(gv+fv)
于是可以 O ( n ) 树 形 d p O(n)树形dp O(n)dp

注意由于中间有 1 y − 1 \frac{1 }{y}-1 y11做分母
所以对于 y = 1 y=1 y=1是无意义的
特判即可

S u b t a s k 3 Subtask3 Subtask3

考虑用类似 S u b t a s k 2 Subtask2 Subtask2的推导方法可以得到

= y n ∑ T G 2 ( T ) ( 1 y − 1 ) ∣ T ∣ =y^n\sum_{T}G^2(T)(\frac 1 y-1)^{|T|} =ynTG2(T)(y11)T
= y n ( 1 y − 1 ) n n 4 ∑ T ∏ i = 1 n − ∣ T ∣ a i 2 n 2 1 y − 1 =y^n\frac{(\frac 1 y-1)^n}{n^4}\sum_T\prod_{i=1}^{n-|T|}\frac{a_i^2n^2}{\frac 1 y-1} =ynn4(y11)nTi=1nTy11ai2n2
考虑枚举 T T T实际是在枚举森林

于是可以列出树的生成函数然后 exp ⁡ \exp exp
k = n 2 1 y − 1 k=\frac{n^2}{\frac 1 y-1} k=y11n2
考虑有标号
所以 f ( x ) = ∑ i = 0 ∞ k i 2 ∗ i i − 2 x i i ! = ∑ i = 0 ∞ k i i x i i ! f(x)=\sum_{i=0}^{\infty}ki^2*i^{i-2}\frac{x^i}{i!}=\sum_{i=0}^{\infty}ki^i\frac{x^i}{i!} f(x)=i=0ki2ii2i!xi=i=0kiii!xi
然后 exp ⁡ \exp exp即可
同样特判 y = 1 y=1 y=1

复杂度 O ( n l o g n ) O(nlogn) O(nlogn)

#include<bits/stdc++.h>
using namespace std;
#define cs const
#define re register
#define pb push_back
#define pii pair<int,int>
#define ll long long
#define fi first
#define se second
#define bg begin
cs int RLEN=1<<20|1;
inline char gc(){
    static char ibuf[RLEN],*ib,*ob;
    (ib==ob)&&(ob=(ib=ibuf)+fread(ibuf,1,RLEN,stdin));
    return (ib==ob)?EOF:*ib++;
}
inline int read(){
    char ch=gc();
    int res=0;bool f=1;
    while(!isdigit(ch))f^=ch=='-',ch=gc();
    while(isdigit(ch))res=(res+(res<<2)<<1)+(ch^48),ch=gc();
    return f?res:-res;
}
inline ll readll(){
    char ch=gc();
    ll res=0;bool f=1;
    while(!isdigit(ch))f^=ch=='-',ch=gc();
    while(isdigit(ch))res=(res+(res<<2)<<1)+(ch^48),ch=gc();
    return f?res:-res;
}
inline int readstring(char *s){
	int top=0;char ch=gc();
	while(isspace(ch))ch=gc();
	while(!isspace(ch)&&ch!=EOF)s[++top]=ch,ch=gc();
	return top;
}
template<class tp>inline void chemx(tp &a,tp b){a<b?a=b:0;}
template<class tp>inline void chemn(tp &a,tp b){a>b?a=b:0;}
cs int mod=998244353;
inline int add(int a,int b){return (a+=b)>=mod?(a-mod):a;}
inline int dec(int a,int b){a-=b;return a+(a>>31&mod);}
inline int mul(int a,int b){static ll r;r=1ll*a*b;return (r>=mod)?(r%mod):r;}
inline void Add(int &a,int b){(a+=b)>=mod?(a-=mod):0;}
inline void Dec(int &a,int b){a-=b,a+=a>>31&mod;}
inline void Mul(int &a,int b){static ll r;r=1ll*a*b;a=(r>=mod)?(r%mod):r;}
inline int ksm(int a,int b,int res=1){if(a==0&&b==0)return 0;for(;b;b>>=1,Mul(a,a))(b&1)&&(Mul(res,a),1);return res;}
inline int Inv(int x){return ksm(x,mod-2);}
inline int fix(int x){return (x<0)?x+mod:x;}
cs int N=100005;
int n,y;
namespace solve1{
	map<pii,int>mp;
	inline void main(){
		for(int i=1;i<n;i++){
			int u=read(),v=read();
			if(u>v)swap(u,v);
			mp[pii(u,v)]=1;
		}
		int tot=n;
		for(int i=1;i<n;i++){
			int u=read(),v=read();
			if(u>v)swap(u,v);
			if(mp.count(pii(u,v)))tot--;
		}
		cout<<ksm(y,tot)<<'\n';
	}
}
namespace solve2{
	vector<int> e[N];
	int f[N],g[N],k,t;
	void dfs(int u,int fa){
		f[u]=1,g[u]=k;
		for(int &v:e[u])if(v!=fa){
			dfs(v,u);
			g[u]=add(mul(g[u],add(f[v],g[v])),mul(f[u],g[v]));
			Mul(f[u],add(f[v],g[v]));
		}
	}
	inline void main(){
		if(y==1){cout<<ksm(n,n-2)<<'\n';return;}
		for(int i=1;i<n;i++){
			int u=read(),v=read();
			e[u].pb(v),e[v].pb(u);
		}
		t=dec(Inv(y),1),k=mul(n,Inv(t));
		dfs(1,0);
		cout<<mul(ksm(y,n),mul(g[1],mul(ksm(t,n),Inv(mul(n,n)))))<<'\n';
	}
}
namespace solve3{
	typedef vector<int> poly;
	cs int C=18,M=(1<<C)|5,G=3;
	int *w[C+1];
	int rev[M],inv[M],ifac[M],fac[M];
	inline void init_rev(int lim){
		for(int i=0;i<lim;i++)rev[i]=(rev[i>>1]>>1)|((i&1)*(lim>>1));
	}
	inline void init_w(){
		for(int i=1;i<=C;i++)w[i]=new int[(1<<(i-1))|1];
		int wn=ksm(G,(mod-1)/(1<<C));w[C][0]=1;
		for(int i=1,l=1<<(C-1);i<l;i++)w[C][i]=mul(w[C][i-1],wn);
		for(int i=C-1;i;i--)
		for(int j=0,l=1<<(i-1);j<l;j++)w[i][j]=w[i+1][j<<1];
		inv[0]=inv[1]=fac[0]=ifac[0]=1;
		for(int i=2;i<M;i++)inv[i]=mul(mod-mod/i,inv[mod%i]);
		for(int i=1;i<M;i++)fac[i]=mul(fac[i-1],i),ifac[i]=mul(ifac[i-1],inv[i]);
	}
	inline void ntt(int *f,int lim,int kd){
		for(int i=0;i<lim;i++)if(rev[i]>i)swap(f[i],f[rev[i]]);
		for(int mid=1,l=1,a0,a1;mid<lim;mid<<=1,l++)
		for(int i=0;i<lim;i+=mid<<1)
		for(int j=0;j<mid;j++)
		a0=f[i+j],a1=mul(w[l][j],f[i+j+mid]),f[i+j]=add(a0,a1),f[i+j+mid]=dec(a0,a1);
		if(kd==-1){
			reverse(f+1,f+lim);
			for(int i=0,iv=inv[lim];i<lim;i++)Mul(f[i],iv);
		}
	}
	inline poly operator *(poly a,poly b){
		int deg=a.size()+b.size()-1;
		if(deg<=32){
			poly c(deg,0);
			for(int i=0;i<a.size();i++)
			for(int j=0;j<b.size();j++)
			Add(c[i+j],mul(a[i],b[j]));
			return c;
		}
		int lim=1;while(lim<deg)lim<<=1;
		init_rev(lim);
		a.resize(lim),ntt(&a[0],lim,1);
		b.resize(lim),ntt(&b[0],lim,1);
		for(int i=0;i<lim;i++)Mul(a[i],b[i]);
		ntt(&a[0],lim,-1),a.resize(deg);
		return a;
	}
	inline poly Inv(poly a,int deg){
		poly b(1,::Inv(a[0])),c;
		for(int lim=4;lim<(deg<<2);lim<<=1){
			init_rev(lim);c.resize(lim>>1);
			for(int i=0;i<(lim>>1);i++)c[i]=(i<a.size()?a[i]:0);
			c.resize(lim),b.resize(lim);
			ntt(&b[0],lim,1),ntt(&c[0],lim,1);
			for(int i=0;i<lim;i++)Mul(b[i],dec(2,mul(b[i],c[i])));
			ntt(&b[0],lim,-1),b.resize(lim>>1);
		}b.resize(deg);return b;
	}
	inline poly deriv(poly a){
		for(int i=0;i+1<a.size();i++)a[i]=mul(a[i+1],i+1);
		a.pop_back();return a;
	}
	inline poly integ(poly a){
		a.pb(0);
		for(int i=a.size()-1;i;i--)a[i]=mul(a[i-1],inv[i]);
		a[0]=0;return a;
	}
	inline poly Ln(poly a,int deg){
		a=integ(Inv(a,deg)*deriv(a)),a.resize(deg);return a;
	}
	inline poly Exp(poly a,int deg){
		poly b(1,1),c;
		for(int lim=2;lim<(deg<<1);lim<<=1){
			c=Ln(b,lim);
			for(int i=0;i<lim;i++)c[i]=dec(i<a.size()?a[i]:0,c[i]);
			Add(c[0],1),b=b*c,b.resize(lim);			
		}b.resize(deg);return b;
	}
	inline void main(){
		if(y==1){cout<<ksm(n,2*(n-2))<<'\n';return;}
		init_w();
		int t=dec(::Inv(y),1),k=mul(n,mul(n,::Inv(t)));
		poly f(n+1,0);
		for(int i=1;i<=n;i++)f[i]=mul(ksm(i,i),mul(k,ifac[i]));
		f=Exp(f,n+1);
		cout<<mul(f[n],mul(fac[n],mul(ksm(mul(t,y),n),ksm(inv[n],4))))<<'\n';
	}
}
int op;
int main(){
	#ifdef Stargazer
	freopen("lx.in","r",stdin);
	#endif
	n=read(),y=read(),op=read();
	if(op==0)solve1::main();
	if(op==1)solve2::main();
	if(op==2)solve3::main();
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值