[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);
}