題目鏈接[SHOI2014]概率充電器
woc我的高考概率是怎麼學的這麼簡單的概率公都忘了233
公式:設事件A,B,p爲概率,則
p(A|B)=p(A)+p(B)-p(A&B)
意思是發生A或B事件之一的概率是發生A的概率+發生B的概率-A和B同時發生概率
那麼一個點x有電的情況爲1:自己給自己充上了電;2:子節點給他充上了電;3:父節點給他充上了電
設事件1的概率爲p[x],那麼1、2使得x有電的概率可以直接使用公式dfs求出來
現在考慮3,我們從父節點貢獻到子節點時要把子節點貢獻給父節點的貢獻減去,公式變一下形即可算出
然後在正向用公式合併即可
注意除法的時候判斷除數大小,被坑了
#include<cmath>
#include<cstdio>
#include<cstdlib>
#include<iostream>
#include<algorithm>
using namespace std;
const int maxn=500010;
int n,m,tot=1,h[maxn];
struct edge{int to,next; double w;}G[maxn*2];
double p[maxn],dp[maxn];
void dfs1(int x,int fat){
for (int i=h[x];i;i=G[i].next){
int v=G[i].to;
if (v==fat) continue;
dfs1(v,x);
p[x]=p[x]+p[v]*G[i].w-p[x]*p[v]*G[i].w;//p(A|B)=p(A)+p(B)-p(A&B);
}
}
void dfs2(int x,int fat){
for (int i=h[x];i;i=G[i].next){
int v=G[i].to;
if (v==fat) continue;
double tmp=1-p[v]*G[i].w;
if (abs(tmp)<1e-10) dp[v]=1;//這裏要注意
else{
double tmp2=(dp[x]-p[v]*G[i].w)/tmp;
dp[v]=p[v]+tmp2*G[i].w-p[v]*tmp2*G[i].w;
}
dfs2(v,x);
}
}
void add(int x,int y,double z){
G[++tot].to=y;G[tot].next=h[x];h[x]=tot;G[tot].w=z;
G[++tot].to=x;G[tot].next=h[y];h[y]=tot;G[tot].w=z;
}
int main(){
scanf("%d",&n);
for (int i=1;i<n;++i){
int x,y; double z;
scanf("%d%d%lf",&x,&y,&z);
z/=100.0; add(x,y,z);
}
for (int i=1;i<=n;++i) scanf("%lf",&p[i]),p[i]/=100.0;
dfs1(1,-1); dp[1]=p[1]; dfs2(1,-1);
double ans=0;
for (int i=1;i<=n;++i) ans+=dp[i];
printf("%.6lf",ans);
}