题意:
著名的电子产品品牌 SHOI 刚刚发布了引领世界潮流的下一代电子产品——概率充电器:
“采用全新纳米级加工技术,实现元件与导线能否通电完全由真随机数决定!SHOI 概率充电器,您生活不可或缺的必需品!能充上电吗?现在就试试看吧!
”
SHOI 概率充电器由 n-1 条导线连通了 n 个充电元件。进行充电时,每条导线是否可以导电以概率决定,每一个充电元件自身是否直接进行充电也由概率决定。
随后电能可以从直接充电的元件经过通电的导线使得其他充电元件进行间接充电。
作为 SHOI 公司的忠实客户,你无法抑制自己购买 SHOI 产品的冲动。在排了一个星期的长队之后终于入手了最新型号的 SHOI 概率充电器。
你迫不及待地将 SHOI 概率充电器插入电源——这时你突然想知道,进入充电状态的元件个数的期望是多少呢?
输入:
第一行一个整数:n。概率充电器的充电元件个数。充电元件由 1-n 编号。
之后的 n-1 行每行三个整数 a, b, p,描述了一根导线连接了编号为 a 和 b 的
充电元件,通电概率为 p%。
第 n+2 行 n 个整数:qi。表示 i 号元件直接充电的概率为 qi%。
输出:
输出一行一个实数,为进入充电状态的元件个数的期望,四舍五入到六位小数
思路:
因为亮既可能从父亲转移,又可能从儿子转移,会比较麻烦(其实就是我不会写)
所以考虑每个点亮不起来的概率。
(x)从儿子(y)或自己转移不亮的概率(dp[1][x]),之间的边通电的概率为(p)
(dp[1][x]=(1-a[x])*prod_{yin{son[x]}}(dp[1][y]+(1-dp[1][y])*(1-p)))
从父亲转移不亮的概率(dp[0][x])
如果是根节点(dp[0][x]=1)
否则,父亲节点为(f) 转移方程为
(q=dp[0][f]*dp[1][f]/(dp[1][x]+(1-dp[1][x])*(1-p)))
↑要去掉从(f)从x这个点转移过来的贡献
(dp[0][x]=q+(1-q)*(1-p))
所以每个节点亮的概率为((1-dp[0][x]*dp[1][x]))
最终结果为(sum_{i=1}^{n}{1-dp[0][i]*dp[1][i]})
#include<bits/stdc++.h>
#define db double
#define M 500005
using namespace std;
db a[M],val[M<<1],dp[2][M],ans,cnt[2][M];//0 表示 从 父亲转移不亮 1表示从儿子(或者自己)转移不亮
int n,to[M<<1],la[M],pr[M<<1],tot;
void add(int x,int y,int p) {
to[++tot]=y,pr[tot]=la[x],la[x]=tot,val[tot]=1.0*p/100;
}
void dfs(int x,int f) {
dp[1][x]=(1-a[x]);
for(int i=la[x]; i; i=pr[i]) {
int y=to[i];
db p=val[i];
if(y==f)continue;
dfs(y,x);
dp[1][x]*=(dp[1][y]+(1-dp[1][y])*(1-p));
}
}
void redfs(int x,int f,db p) {
if(f) {
db q=dp[0][f]*dp[1][f]/(dp[1][x]+(1-dp[1][x])*(1-p));
dp[0][x]=q+(1-p)*(1-q);
} else dp[0][x]=1;
ans+=1-dp[0][x]*dp[1][x];//加上亮的概率
for(int i=la[x]; i; i=pr[i]) {
int y=to[i];
if(y==f)continue;
redfs(y,x,val[i]);
}
}
int main() {
scanf("%d",&n);
for(int i=1; i<n; i++) {
int x,y,p;
scanf("%d%d%d",&x,&y,&p);
add(x,y,p),add(y,x,p);
}
for(int i=1; i<=n; i++)scanf("%lf",&a[i]),a[i]/=100;
dfs(1,0),redfs(1,0,0);
printf("%.6f
",ans);
return 0;
}