WC模拟 : Hello my friend(树形期望DP)

题意:
给一棵树,每个点有黑白两种颜色之一,从根节点(度数大于 1 1 )开始随机游走。 你有一个计数器,初始为0,然后反复执行一下过程:
1.如果当前点为黑色或者第一次经过,则计数器值加 1 1
2.如果当前所在点度数为1,结束这个过程。
3.等概率选取一个和当前所在点直接相连的点走过去。

求计数器最后数字的期望,对 998244353 998244353 取模。

题解:
这么经典的题以前居然没做过。。

对于黑白两种颜色分开考虑:
pi p i i i 号点在树上的父亲。
对于黑点,此时贡献为期望经过次数,采用期望DP的方式计算,则有:
fi=colori+j,(i,j)Vfjdegi f i = color i + ∑ j , ( i , j ) ∈ V f j d e g i
(叶子节点单独考虑)。

这时候发现拓扑图有环,这里有一种树上不用高斯消元的经典做法:
fi=kifpi+bi f i = k i f p i + b i .
DP DP 时儿子节点的 fj f j 可以用 kjfi+bj k j f i + b j 的形式表达出来,然后就会得到一个只与 fi,fpi f i , f p i 有关的方程,就可以递推上去了, b1 b 1 即是答案。

下面考虑白点的贡献,白点的贡献为每个白点经过的概率,做树上概率 DP DP 又有一种经典的方式:

fi f i 为从 i i 点出发,走到pi的概率。
gi g i 为从 pi p i 出发,走到 i i 的概率。

有:
fi=1degi+j,(i,j)V,jpifjfidegi

gi=1degpi+j,(pi,j)V,ji,jppifj·gidegpi+gpigidegpi g i = 1 d e g p i + ∑ j , ( p i , j ) ∈ V , j ≠ i , j ≠ p p i f j · g i d e g p i + g p i g i d e g p i

那么这道题就得到了解决。

#include<bits/stdc++.h>
typedef long long ll;
using namespace std;
const int R_LEN=(1<<18)|1;
char ibuf[R_LEN],*s,*t;
inline char getc(){
    (s==t)&&(t=(s=ibuf)+fread(ibuf,1,R_LEN,stdin));
    return (s==t)?-1:*s++;
}
inline int rd(){
    char ch=getc(); int i=0,f=1;
    while(!isdigit(ch)){if(ch=='-')f=-1; ch=getc();}
    while(isdigit(ch)){i=(i<<1)+(i<<3)+ch-'0'; ch=getc();}
    return i*f;
}
inline void rd(char *c){
    char ch=getc(); 
    while(isspace(ch)) ch=getc();
    while(!isspace(ch)) *c++=ch, ch=getc();
    *c=0; return;
}
const int Mod=998244353;
const int N=1e5+50;
int cnt=0;
inline int power(int a,int b){
    int rs=1; a=a%Mod;
    for(;b;b>>=1,a=(ll)a*a%Mod) if(b&1) rs=(ll)rs*a%Mod;
    return rs;
}
int n,k[N],b[N],f[N],gi[N],col[N],dp[N];
int deg[N],g[N],nt[N*2],vt[N*2],ec;
char ch[N];
inline void adde(int x,int y){nt[++ec]=g[x]; g[x]=ec; vt[ec]=y;}
inline void dfs(int x,int fa){
    ++cnt;
    if(deg[x]==1){
        k[x]=0; b[x]=col[x]; 
        f[x]=0; return;
    }
    int sumk=0, sumb=0, sumf=0;
    for(int e=g[x]; e; e=nt[e]){
        if(vt[e]==fa) continue;
        dfs(vt[e], x);
        sumk=(sumk+k[vt[e]])%Mod; 
        sumb=(sumb+b[vt[e]])%Mod;
        sumf=(sumf+f[vt[e]])%Mod;
    }
    f[x]=power((deg[x]-sumf+Mod)%Mod, Mod-2);
    sumf=(col[x]*deg[x]+sumb)%Mod;
    sumb=(deg[x]-sumk+Mod)%Mod;
    sumb=power(sumb, Mod-2);
    k[x]=sumb; b[x]=(ll)sumf*sumb%Mod;
}
inline void dfs2(int x,int fa){
    if(deg[x]==1) return;
    int sum=gi[x]; 
    for(int e=g[x]; e; e=nt[e]){
        if(vt[e]==fa)continue; 
        sum=(sum+f[vt[e]])%Mod;
    }
    for(int e=g[x]; e; e=nt[e]){
        if(vt[e]==fa)continue;
        int tp=((deg[x]-sum+Mod)%Mod+f[vt[e]])%Mod;
        gi[vt[e]]=power(tp,Mod-2);
        dp[vt[e]]=(ll)dp[x]*gi[vt[e]]%Mod;
    }
    for(int e=g[x]; e; e=nt[e]){
        (vt[e]!=fa) && (dfs2(vt[e],x),0);
    }
}
int main(){
    n=rd(); rd(ch+1); 
    for(int i=1; i<=n; i++) col[i]=(ch[i]=='0')? 0: 1;
    for(int i=1; i<n; i++){
        int x=rd(), y=rd(); 
        adde(x,y); adde(y,x);
        ++deg[x]; ++deg[y];
    } dp[1]=1;
    dfs(1,0); dfs2(1,0);
    cerr<<cnt<<endl;
    int ans=b[1]; 
    for(int i=1;i<=n;i++)
        ans=(ans+dp[i]*(!col[i]))%Mod;
    printf("%d\n",ans);
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值