P5206 数树 WC2019 Solution

本题包含三个问题:

  • 问题 0:已知两棵 n 个节点的树的形态(两棵树的节点标号均为 1n),其中第一棵树是红树,第二棵树是蓝树。要给予每个节点一个 [1,y] 中的整数,使得对于任意两个节点 p,q,如果存在一条路径 (a1=p,a2,,am=q) 同时属于这两棵树,则 p,q 必须被给予相同的数。求给予数的方案数。
    • 存在一条路径同时属于这两棵树的定义见「题目背景」。
  • 问题 1:已知蓝树,对于红树的所有 nn2 种选择方案,求问题 0 的答案之和。
  • 问题 2:对于蓝树的所有 nn2 种选择方案,求问题 1 的答案之和。

y=1 的情况:

  • opt=0:1
  • opt=1:nn2
  • opt=2:n2n4

不妨设边集为 T1,T2

问题0:显然也就是两棵树的公共边保留,答案也就是 ync,其中 c 是公共边条数。

问题1:

T2yn|T1T2|

g(T)=T2[TT1T2],也即包含边集 T 的树个数,可以假定 Tn|T| 个连通块构成,大小分别为 a1an|T|,则 g(T)=nn|T|2i=1n|T|ai

T2yn|T1T2|=T2TT1T2ST(1)|T||S|yn|S|(子集反演)=Tg(T)STyn|S|(1)|T||S|=Tg(T)yn|T|ST(1)|T||S|y|T||S|=Tg(T)yn|T|ST(y)|T||S|=Tg(T)yn|T|(1y)|T|(二项式定理)=i=1kai=nnk2yk(1y)nki=1kai(Purfer 序列)=(1y)nn2i=1kai=ni=1kai·n·y1y

i=1kai=ni=1kai·n·y1y 可以由原树进行树形DP求得。

即:每个点点权为 ny1y,要求将原树划分为若干个连通块,划分方案的权值定义为各个连通块点权和之积,求所有划分方案权值和。

定义 fi,0/1 为子树 i 当前连通块是否已经选择的权值和。

容易得到:

fu,0fu,0·fv,0+fu,0·fv,1

fu,1fu,1·fv,0+fu,1·fv,1+fu,0·fv,1

初始化 fu,0=1,fu,1=ny1y

问题2:现在连原树也没了。。。

其实上式也符合,因为两棵树都没有,所以上式的 g(T) 改为 g2(T) 即可。

T1,T2yn|T1T2|=T1,T2TT1T2ST(1)|T||S|yn|S|=Tg2(T)STyn|S|(1)|T||S|=Tg2(T)yn|T|ST(1)|T||S|y|T||S|=Tg2(T)yn|T|ST(y)|T||S|=Tg2(T)yn|T|(1y)|T|=i=1kai=nn2k4yk(1y)nki=1kai2=(1y)nn4i=1kai=ni=1kai2·n2·y1y

哦豁,现在就是后式求和怎么玩了。。。

考虑每个连通块,大小为 ai 的树有 aiai2,所以对于一组 [a1,a2ak] 会有 i=1kaiai2 个不同局面。(有标号MSET构造)

所以所求:

i=1kai=ni=1kai2·n2·y1y=[zn]exp(i=0iin2yi!)=[zn]k1k!(iiin2yi!)k

所以答案为:

(1y)nn4[zn]exp(kkkn2yk!)

#include<bits/stdc++.h>
using namespace std;
#define int long long
const int p=998244353;
int n,y,op;
int power(int a,int b){
    int res=1;a%=p;b%=(p-1);
    while(b){
        if(b&1)res=res*a%p;a=a*a%p;b>>=1;
    }
    return res;
}
namespace Soly1{
    void sol(){
        if(op==0)cout<<"1";
        else if(op==1)cout<<power(n,n-2);
        else cout<<power(n,(n-2)<<1);
    }
}
namespace Sol0{
    #define pr pair<int,int>
    #define mk make_pair
    map<pr,int>h;
    void sol(){
        for(int i=1;i<n;i++){
            int u,v;cin>>u>>v;if(u>v)swap(u,v);
            h[mk(u,v)]=1;
        }
        int t=0;
        for(int i=1;i<n;i++){
            int u,v;cin>>u>>v;if(u>v)swap(u,v);
            t+=h[mk(u,v)];
        }
        cout<<power(y,n-t);
    }
}
namespace Sol1{
    int f[N][2],w;
    vector<int>e[N];
    void dfs(int u,int fa){
        f[u][0]=1,f[u][1]=w;
        for(auto v:e[u]){
            if(v==fa)continue;
            dfs(v,u);
            int a=f[u][0]*f[v][0]%p+f[u][0]*f[v][1]%p;
            int b=f[u][1]*f[v][0]%p+f[u][1]*f[v][1]%p+f[u][0]*f[v][1]%p;
            f[u][0]=a%p;
            f[u][1]=b%p;
        }
    }
    void sol(){
        for(int i=1;i<n;i++){
            int u,v;cin>>u>>v;e[u].push_back(v);e[v].push_back(u);
        }
        w=n*y%p*power(1-y,p-2)%p;
        dfs(1,0);
        int res=power(1-y,n)*power(n*n,p-2)%p*f[1][1]%p;
        res=(res%p+p)%p;
        cout<<res<<"\n";
    }
}
namespace Sol2{
    
}
posted @   spdarkle  阅读(11)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!
点击右上角即可分享
微信分享提示