[SHOI2014] 概率充电器 (概率 dp)

Problem

link
一棵树,每个点有自己来电的概率 \(q[i]\),每条边有导电的概率 \(p[i]\)
求有电结点的期望个数,也就是概率。

Solution

不会容斥,于是反着求每个节点不来电的概率。

首先这个需要换根,我们先考虑换根前的 dp 。

考虑有 \(f[u]\) 表示仅考虑 \(u\) 的儿子, \(u\) 不通电的概率。

那么有:

\[f[u] = (1 - q[u]) \times \prod_{v\in son[u]}(f[v] + (1-f[v])\times (1-p(u,v))) \]

代表这个电不自己来电,儿子要么没电,要么不导电。

接下来考虑换根:

\(g[u]\) 表示仅考虑父亲,\(u\) 不来电的概率。

那么

\[tmp = f[u] + (1 - f[u]) \times (1 - p(fa[u],u)) \]

这是这个结点贡献给 \(fa[u]\) 的,要扣掉,但是要特判他没有贡献,不然除数为 \(0\)

\[res = g[fa[u]] \times (\cfrac{f[fa[u]]}{tmp}) \]

这个是它爹没有电的概率。

那么

\[g[u] = res + (1 - res) \times (1 - p(fa[u],u)) \]

转移式就跟 \(f\) 数组大同小异了。

所以最后对于每个结点 \(u\) , \(f[u]\times g[u]\) 就是它不通电的概率。
答案就是 \(\sum_{1}^{n}(1 - f[i]\times g[i])\)

#include<bits/stdc++.h>
#define rep(i,a,b) for(int i=(a);i<=(b);++i)
#define Rep(i,a,b) for(int i=(a);i<(b);++i)
#define rrep(i,a,b) for(int i=(a);i>=(b);--i)
using namespace std;
template <typename T>
inline void read(T &x){
    x=0;char ch=getchar();bool f=0;
    while(ch<'0'||ch>'9'){if(ch=='-')f=1;ch=getchar();}
    while(ch>='0'&&ch<='9')x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
    if(f)x=-x;
}
template <typename T,typename ...Args>
inline void read(T &tmp,Args &...tmps){read(tmp);read(tmps...);}
const int N = 5e5 + 5;
int n,q[N],head[N];
struct qwq{
    int to,nxt;double w;
}e[N<<1];
int cnt;
inline void add(int u,int v,int w){
    e[++cnt] = {v,head[u],0.01*w};
    head[u] = cnt;
}
double f[N],g[N];
void dfs1(int u,int fa){
    f[u] = 1.0 - 0.01 * q[u];
    for(int i=head[u];i;i=e[i].nxt){
        int v = e[i].to;double w = e[i].w;
        if(v == fa)continue;
        dfs1(v,u);
        f[u] *= (f[v] + (1 - w) * (1 - f[v]));
    }
}
void dfs2(int u,int fa){
    if(u == 1)g[u] = 1;
    for(int i=head[u];i;i=e[i].nxt){
        int v = e[i].to;
        double w  = e[i].w;
        if(v == fa)continue;
        double tmp = f[v] + (1 - w) * (1 - f[v]),res;
        if(tmp > 0)
        res = g[u] * f[u] / (f[v] + (1 - w) * (1 - f[v]));
        else res = 0;
        g[v] = res + (1 - res) * (1 - w);
        dfs2(v,u);
    }
}
signed main(){  
    read(n);
    rep(i,2,n){
        int u,v,w;
        read(u,v,w);
        add(u,v,w);add(v,u,w);
    }
    rep(i,1,n)read(q[i]);
    dfs1(1,0);
    dfs2(1,0);
    double ans = 0;
    rep(i,1,n)ans += 1 - f[i] * g[i];
    printf("%.6lf",ans);
}
posted @ 2022-09-14 20:46  Xu_brezza  阅读(90)  评论(0编辑  收藏  举报