题目大意
有n个点的树,第i个节点有Xi的概率直接充电,第j条边有Yj的概率导电。
求期望有电的结点数。
题解
又是期望?
怎么直接求有电的期望?由于要考虑很多事件的集合上的关系,直接求不太客观。
那就求每个点没电的期望。
看一下题目给的条件:
①每个点可以向它的父亲导电,也可以向它的儿子导电。(直接进行树形DP看上去很难,因为父亲/儿子都可以转移给x)
②考虑什么情况下点x会有电。当且仅当x自己导电或父亲/儿子有电且与x的连边导电。
从上面的文段可知,转移给x的只有2种情况:
①fa[x]转移给x。②son[x]转移给x。
由于这是概率DP,我们又知P(A)*P(B)=P(A∩B),所以我们可以考虑只有①情况的概率,还有只有②的概率,两种情况的积就是期望没电的概率。
设:
f[x]
表示不考虑fa[x]的情况下x没电的概率,
g[x]
表示fa[x]没有传电给x的概率。
这样子就够了?
不行。根据以上的
f[x]
和
g[x]
因为你一定会想到:
g[x]=f[fa[x]]∗g[fa[x]]+(1−f[fa[x]]∗g[fa[x]])∗(1−相应导线的导电概率)
然而
f[fa[x]]∗g[fa[x]]
是包含了x会影响fa[x]的情况,这样子算的概率会重复。
所以我们要排除x对fa[x]的干扰。
所以,设
h[x]
表示x对fa[x]的不导电的概率。
那么
f[fa[x]]∗g[fa[x]]/h[x]
就排除了干扰。PS:注意
h[x]
可能等于0,这种情况下
g[x]=1−相应导线的导电概率
,意思是x一定会导电给fa[x]。
注意一下 h[x]=0 的情况。
最后, ans=Σ1−f[i]∗g[i] 。
对这道题(概率DP)的心得:
①遇到问题像每一个点只属于两种状态A,B的,无论是求概率还是求方案数:
如果求属于A的情况要讨论有很多种情况或是式子很复杂,那么看下:
如果属于B的情况可以由很多个子条件的并集表示,那么尽量去考虑B。
进而A的方案数/概率就是所有情况-B的。
②有多种途径转移到这个状态的,那么可以根据概率的公式,分别考虑每一种情况,然后再合起来。
③对于树形DP的实现方法,我们一定要知道各语句的执行顺序,保证被转移的状态已经是最优的。具体来说,就是先dfs还是先算f[]。
代码
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#define N 500010
#define DB double
#define fo(i,a,b) for(i=a;i<=b;i++)
using namespace std;
struct note{
int to,next;
DB val;
};note edge[N*2];
int i,j,n,k;
int tot,u,v,w;
int head[N];
int fa[N];
DB f[N],g[N],h[N],x[N];
DB ans,temp;
int read(){
int fh=1,rs=0;char ch;
while((ch<'0'||ch>'9')&& ch!='-')ch=getchar();
if(ch=='-')fh=-1,ch=getchar();
while(ch>='0'&&ch<='9')rs=rs*10+ch-'0',ch=getchar();
return rs;
}
void lb(int x,int y,DB z){
edge[++tot].to=y;
edge[tot].next=head[x];
edge[tot].val=z;
head[x]=tot;
}
void dg(int x){
for(int i=head[x];i;i=edge[i].next)
if(fa[x]!=edge[i].to){
fa[edge[i].to]=x;
dg(edge[i].to);
h[edge[i].to]=f[edge[i].to]+(1.0-f[edge[i].to])*(1.0-edge[i].val);
f[x]*=h[edge[i].to];
}
}
void dg1(int x){
for(int i=head[x];i;i=edge[i].next)
if(fa[x]!=edge[i].to){
if(h[edge[i].to]<1e-8)temp=0.0;
else temp=g[x]*f[x]/h[edge[i].to];
g[edge[i].to]=temp+(1.0-temp)*(1.0-edge[i].val);
dg1(edge[i].to);
}
}
int main(){
n=read();
fo(i,1,n-1){
u=read(),v=read(),w=read();
temp=(DB)w/100;
lb(u,v,temp);
lb(v,u,temp);
}
fo(i,1,n){
temp=read();
x[i]=(DB)temp/100;
f[i]=1.0-x[i];
}
g[1]=1;
dg(1);
dg1(1);
fo(i,1,n)ans=ans+1-f[i]*g[i];
printf("%.6lf",ans);
return 0;
}