著名的电子产品品牌SHOI 刚刚发布了引领世界潮流的下一代电子产品—— 概率充电器:
“采用全新纳米级加工技术,实现元件与导线能否通电完全由真随机数决 定!SHOI 概率充电器,您生活不可或缺的必需品!能充上电吗?现在就试试看 吧!”
SHOI 概率充电器由n-1 条导线连通了n 个充电元件。进行充电时,每条导 线是否可以导电以概率决定,每一个充电元件自身是否直接进行充电也由概率 决定。随后电能可以从直接充电的元件经过通电的导线使得其他充电元件进行 间接充电。
作为SHOI 公司的忠实客户,你无法抑制自己购买SHOI 产品的冲动。在排 了一个星期的长队之后终于入手了最新型号的SHOI 概率充电器。你迫不及待 地将SHOI 概率充电器插入电源——这时你突然想知道,进入充电状态的元件 个数的期望是多少呢?
Solution
这题要算期望个数,实际上我们可以直接算出每个原件充电的概率,求和就是期望个数(显然)。
考虑一个元件充电的方式,1.自动充电,2.被儿子充电,3.被父亲充电。
第一次DFS我们要求出后两种方式的概率。
第二次我们要求出节点被父亲充电的概率。
注意要三个条件只要满足一个就可以。
考虑两个时间A和B,我们要求他们必需都发生,那么概率为A*B。
如果需要至少一个发生呢?1-(1-A)*(1-B)。化简后为A+B-A*B
注意到这不只是简单相加,因为如果简单相加的话,就变成A*1+B*1,这个1指的是B/A的所有可能。
问题来了,A和B都发生的概率被算了两次,所以我们要把他减掉。
那么这题的转移方程就容易得出了。
#include<iostream> #include<cstdio> #include<vector> #include<map> #define N 500005 using namespace std; const double eps=1e-15; double f[N],ans,g[N]; int a[N],tot,head[N],n; struct zzh{ int n,to; double l; }e[N<<1]; inline void add(int u,int v,int l){ e[++tot].n=head[u]; e[tot].to=v; head[u]=tot; e[tot].l=(double)l/100; } void dfs1(int u,int fa){ double an=1.0-(double)a[u]/100; for(int i=head[u];i;i=e[i].n)if(e[i].to!=fa){ int v=e[i].to; dfs1(v,u); an*=(1.0-f[v]*e[i].l); } f[u]=1-an; } void dfs2(int u,int fa){ for(int i=head[u];i;i=e[i].n)if(e[i].to!=fa){ int v=e[i].to; double ip=(1-f[v]*e[i].l); if(ip<eps)ip=1; g[v]=(1.0-(1-f[u])/ip*(1-g[u]))*e[i].l; dfs2(v,u); } } int main(){ scanf("%d",&n);int u,v,w; for(int i=1;i<n;++i){ scanf("%d%d%d",&u,&v,&w); add(u,v,w); add(v,u,w); } for(int i=1;i<=n;++i)scanf("%d",&a[i]); dfs1(1,0); dfs2(1,0); for(int i=1;i<=n;++i)ans+=1-(1-f[i])*(1-g[i]); printf("%lf",ans); return 0; }
f[i]=1−(1−Pi)×∏v∈son[i](1−f[v]×Pi,v)
f[i]=1−(1−Pi)×∏v∈son[i](1−f[v]×Pi,v)
f[i]=1−(1−Pi)×∏v∈son[i](1−f[v]×Pi,v)