原文改自tzf的博客
首先普及一个概率公式 P(A+B)=P(A)+P(B)-P(AB)
题意:一些充电元件和导线构成一棵树,充电元件是否能充电有2种情况,
1、它自己有qi%的概率充电
2、与它相邻的元件通过导线给它充电(导线有p%的概率导通)
求最终充了电的元件的期望
题解:首先可以将元件能否充电分成3种情况考虑,
1、它自己给自己充好了电
2、它的儿子方向给它传送了电
3、它的父亲方向给它传送了电。
对于1,题目已经给出可以直接赋值,
对于2,可以通过一次树的深度遍历求得。pson[now]=pson[now] + pson[to]*edge_p - pson[now]*pson[to]*edge_p
对于3,麻烦一点,因为2中我们已经求得当前点(now)的儿子们给当前点的贡献,现在要求当前点给它的某个儿子(to)的贡献,这里要计算的话,就必须要排除之前to->now的,(想想若2中计算了to->now的概率,现在又要计算now->to的概率,明显出现了循环)具体公式推导见代码
P(A)=( P(A+B)-P(B) )/(1-P(B));
#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <cmath>
using namespace std;
#define maxn 510000
double pson[maxn],pp[maxn],dp[maxn],ans;
struct node
{
int v;
double p;
}e[maxn*2];
int en,n,first[maxn],next[maxn*2];
void add(int a,int b,double c)
{
en++;
e[en].v=b;
e[en].p=c;
next[en]=first[a];
first[a]=en;
}
void dfs1(int now,int fa)
{
int v;
for(int i=first[now];i;i=next[i])
{
v=e[i].v;
if(fa==v) continue;
dfs1(v,now);
pson[now]=pson[now]+e[i].p*pson[v]-e[i].p*pson[v]*pson[now];
}
}
void dfs2(int now,int fa)
{
ans+=dp[now];
int v;
for(int i=first[now];i;i=next[i])
{
v=e[i].v;double p=e[i].p;
if(fa==v) continue;
double tmp=(1-pson[v]*p);
if(fabs(tmp)<1e-8) dp[v]=1.0;
else
{
double y=(dp[now]-pson[v]*p)/(1.0-pson[v]*p);
dp[v]=pson[v]+y*p-pson[v]*y*p;
}
dfs2(v,now);
}
}
int main()
{
int a,b,c;
scanf("%d",&n);
en=0;
for(int i=1;i<n;i++)
{
scanf("%d%d%d",&a,&b,&c);
add(a,b,c/100.0);
add(b,a,c/100.0);
}
for(int i=1;i<=n;i++)
{
scanf("%lf",&pson[i]);
pson[i]/=100.0;
}
dfs1(1,-1);
dp[1]=pson[1];
dfs2(1,-1);
printf("%.6f\n",ans);
return 0;
}