[SHOI2014]概率充电器
题目描述
著名的电子产品品牌SHOI 刚刚发布了引领世界潮流的下一代电子产品—— 概率充电器:
“采用全新纳米级加工技术,实现元件与导线能否通电完全由真随机数决 定!SHOI 概率充电器,您生活不可或缺的必需品!能充上电吗?现在就试试看 吧!”
SHOI 概率充电器由n-1 条导线连通了n 个充电元件。进行充电时,每条导 线是否可以导电以概率决定,每一个充电元件自身是否直接进行充电也由概率 决定。随后电能可以从直接充电的元件经过通电的导线使得其他充电元件进行 间接充电。
作为SHOI 公司的忠实客户,你无法抑制自己购买SHOI 产品的冲动。在排 了一个星期的长队之后终于入手了最新型号的SHOI 概率充电器。你迫不及待 地将SHOI 概率充电器插入电源——这时你突然想知道,进入充电状态的元件 个数的期望是多少呢?
输入输出格式
输入格式:
第一行一个整数:n。概率充电器的充电元件个数。充电元件由1-n 编号。
之后的n-1 行每行三个整数a, b, p,描述了一根导线连接了编号为a 和b 的 充电元件,通电概率为p%。
第n+2 行n 个整数:qi。表示i 号元件直接充电的概率为qi%。
输出格式:
输出一行一个实数,为能进入充电状态的元件个数的期望,四舍五入到小 数点后6 位小数。
输入输出样例
说明
对于30%的数据,n≤5000。
对于100%的数据,n≤500000,0≤p,qi≤100。
非常好的一道概率题。
首先介绍两个公式(虽然可能都比较显然2333):
1. P(A+B) = P(A) + P(B) -P(AB) [和容斥是一个原理,把多加的减回来]
这里面P(A+B)表示A或者B事件发生,P(AB)表示A事件发生且B事件发生。
这个是用来合并两个事件的。
2.P(A) * (1 - P(B)) = P(A+B) - P(B) [等号两边都代表A事件发生且B事件不发生的概率]
这个是用来把一个事件从包含它的一个事件中分离的。
我们可以设 第i个元件被点亮的概率是 f(i) ,再设 仅考虑i这个子树(默认1是根) i被点亮的概率是 g(i)。
显然g更好求,因为它没有后效性。
那么我们就先一遍dfs把g求出来。
首先 f(1)=g(1) ,然后考虑其他的f(i)怎么求。用语言描述的话,f(i) = (仅考虑子树它被点亮 或者 被父亲点亮) 的概率。
显然求f (i) 只需要合并一下 g(i) 与 它被父亲点亮的概率。
那么父亲点亮它的概率是 f(fa) * p(fa,i) 吗?
并不是,因为这里的 f(fa) 还包含着 g(i) 对它的贡献,所以我们要把 fa 被点亮这个大事件 中的 fa 被 i点亮这个小事件分离出来,然后 * p(fa,i) 之后再与g[i]合并得到 f[i]。
#include<bits/stdc++.h> #define ll long long #define maxn 500005 #define D double using namespace std; D f[maxn],g[maxn],val[maxn*2],A; // g 是不考虑父亲的答案,f g+父亲后的答案 int n,m,to[maxn*2],ne[maxn*2],hd[maxn]; inline int read(){ int x=0; char ch=getchar(); for(;!isdigit(ch);ch=getchar()); for(;isdigit(ch);ch=getchar()) x=x*10+ch-'0'; return x; } inline D merge(D x,D y){ return x+y-x*y; } inline D split(D x,D y){ //考虑 P(A)*(1-P(B)) = P(A+B)-P(B) [都代表选A且不选B的概率] //我们要做的就是把P(A)从P(A+B)中分离出来 return (x-y)/(1-y); } void dfs(int x,int fa){ for(int i=hd[x];i;i=ne[i]) if(to[i]!=fa){ dfs(to[i],x); g[x]=merge(g[x],g[to[i]]*val[i]); } } void rever(int x,int fa){ A+=f[x]; for(int i=hd[x];i;i=ne[i]) if(to[i]!=fa){ D t=val[i]*g[to[i]]; if(t==1.00) f[to[i]]=1; else f[to[i]]=merge(g[to[i]],split(f[x],t)*val[i]); rever(to[i],x); } } int main(){ int uu,vv,ww; scanf("%d",&n); for(int i=1;i<n;i++){ uu=read(),vv=read(),ww=read(); to[i]=vv,ne[i]=hd[uu],hd[uu]=i,val[i]=(double)ww/100.00; to[i+n]=uu,ne[i+n]=hd[vv],hd[vv]=i+n,val[i+n]=val[i]; } for(int i=1;i<=n;i++){ ww=read(); g[i]=(double)ww/100.00; } dfs(1,1),f[1]=g[1]; rever(1,1); printf("%.6lf\n",A); return 0; }