题目大意: 给出一棵树,第 i i i 个节点有 a i a_i ai 的概率有电,第 i i i 条边有 e i e_i ei 的概率导电,问期望有几个节点有电。
题解
很经典的 d p dp dp 套路,第一次 d p dp dp 求出 f [ i ] f[i] f[i],表示子树对 i i i 的贡献,也就是只看子树的情况下,自己有电的概率。然后第二次 d p dp dp 时带着祖先以及兄弟们的贡献跑即可。
代码如下:
#include <cstdio>
#define maxn 500010
int n;
double a[maxn];
struct edge{int y,next;double z;};
edge e[maxn<<1];
int first[maxn],len=0;
void buildroad(int x,int y,int z)
{
e[++len]=(edge){y,first[x],z/100.0};
first[x]=len;
}
double f[maxn];
void dfs1(int x,int fa)
{
f[x]=1.0-a[x];//先记录没电的概率
for(int i=first[x];i;i=e[i].next)
if(e[i].y!=fa)dfs1(e[i].y,x),f[x]*=1.0-f[e[i].y]*e[i].z;
//所有没电的概率乘起来,再用1减,就是有电的概率了
f[x]=1.0-f[x];
}
double ans=0.0;
void dfs2(int x,int fa,double FA)
{
ans+=1.0-(1.0-f[x])*(1.0-FA);
for(int i=first[x];i;i=e[i].next)
if(e[i].y!=fa)
{
if(e[i].z!=0.0&&f[e[i].y]!=1.0)//这两个是用来判掉除0的情况
dfs2(e[i].y,x,(1.0-(1.0-f[x])/(1.0-f[e[i].y]*e[i].z)*(1.0-FA))*e[i].z);
else dfs2(e[i].y,x,0.0);
}
}
int main()
{
scanf("%d",&n);
for(int i=1,x,y,z;i<n;i++)
scanf("%d %d %d",&x,&y,&z),
buildroad(x,y,z),buildroad(y,x,z);
for(int i=1;i<=n;i++)scanf("%lf",&a[i]),a[i]/=100.0;
dfs1(1,0);dfs2(1,0,0.0);
printf("%.6lf",ans);
}