zoukankan      html  css  js  c++  java
  • 【LuoguP5206】[WC2019] 数树

    题目链接

    题意

    定义 (F(T_1,T_2)=y^{n-common}) 其中 (common) 为两棵树 (T_1,T_2) 的公共边条数。

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

    Sol

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

    这道题的关键在第二问。

    考虑要求的式子:
    我们令 (c) 是重边条数。

    [ans=sum_{T_2} y^{n-c}=y^nsum_{T_2}y^{-c} ]

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

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

    [ans= y^nsum (y^{-1}-1+1)^c\ ans= y^nsum sum_{i=0}^c {cchoose i}(y^{-1}-1)^i\ ]

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

    这样子答案就变成了:

    [ans=y^nsum_{Esubseteq E_{T_1}} (y^{-1}-1)^{|E|}*F(E) ]

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

    这个大力用(prufer)序列推式子可以得出:
    (F(E)=n^{m-2}prod_{i=1}^m size_i)
    (m) 是边集形成的连通块个数。

    回代:

    [ans=y^nsum_{Esubseteq E_{T_1}} (y^{-1}-1)^{|E|}n^{m-2}prod_{i=1}^m size_i ]

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

    [ans=(1-y)^n*n^{-2}sum_{Esubseteq E_{T_1}} igg((y^{-1}-1)^{-1}nigg)^mprod_{i=1}^m size_i ]

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

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

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

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

    答案的式子就是多了个枚举:

    [ans=(1-y)^n*n^{-2}sum_{T_1}sum_{Esubseteq E_{T_1}} igg((y^{-1}-1)^{-1}nigg)^mprod_{i=1}^m size_i ]

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

    [ans=(1-y)^n*n^{-2}sum_{E} F(E)igg((y^{-1}-1)^{-1}nigg)^mprod_{i=1}^m size_i ]

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

    [ans=(1-y)^n*n^{-2}sum_{E} n^{m-2}igg((y^{-1}-1)^{-1}nigg)^mprod_{i=1}^m size_i^2 ]

    [ans=(1-y)^n*n^{-4}sum_{E} igg(n^2(y^{-1}-1)^{-1}igg)^mprod_{i=1}^m size_i^2 ]

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

    [ans=Bsum_{E} K^mprod_{i=1}^m size_i^2 ]

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

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

    [sum_{i=1}^n G(x)^i ]

    注意到我们会算重,那么应该是:

    [sum_{i=1}^n frac{G(x)^i}{i!} ]

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

    多项式 (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
    ",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
    ",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
    ",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
    ",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;
    }
    
    
  • 相关阅读:
    链表--判断一个链表是否为回文结构
    矩阵--“之”字形打印矩阵
    二叉树——平衡二叉树,二叉搜索树,完全二叉树
    链表--反转单向和双向链表
    codeforces 490C. Hacking Cypher 解题报告
    codeforces 490B.Queue 解题报告
    BestCoder19 1001.Alexandra and Prime Numbers(hdu 5108) 解题报告
    codeforces 488A. Giga Tower 解题报告
    codeforces 489C.Given Length and Sum of Digits... 解题报告
    codeforces 489B. BerSU Ball 解题报告
  • 原文地址:https://www.cnblogs.com/NeosKnight/p/10751324.html
Copyright © 2011-2022 走看看