bzoj 3566: [SHOI2014]概率充电器
Description
著名的电子产品品牌 SHOI 刚刚发布了引领世界潮流的下一代电子产品——概率充电器:
“采用全新纳米级加工技术,实现元件与导线能否通电完全由真随机数决定!SHOI 概率充电器,您生活不可或缺的必需品!能充上电吗?现在就试试看吧!”
SHOI 概率充电器由 n-1 条导线连通了 n个充电元件。进行充电时,每条导线是否可以导电以概率决定,每一个充电元件自身是否直接进行充电也由概率决定。
随后电能可以从直接充电的元件经过通电的导线使得其他充电元件进行间接充电。
作为 SHOI 公司的忠实客户,你无法抑制自己购买 SHOI产品的冲动。在排了一个星期的长队之后终于入手了最新型号的 SHOI 概率充电器。
你迫不及待地将 SHOI 概率充电器插入电源——这时你突然想知道,进入充电状态的元件个数的期望是多少呢?
solution
做一个转化,转化为求不能进入充电状态的概率,最后用1减去即可
那么设 \(f[x]\) 表示x不能被进入充电状态的概率,先考虑子树内部:
\(f[x]=(1-q[i])*\prod{((1-f[u])*(1-p[i])+f[u])}\)
上面的转移方程意思为分两种情况:
1.子节点内部没有进入充电状态 \(f[u]\)
2.子节点内部已经进入充电状态,但是边断掉了 \((1-f[u])*(1-p[i])\)
最后从根下放再做一遍即可
#include <algorithm>
#include <iostream>
#include <cstdlib>
#include <cstring>
#include <cstdio>
#include <cmath>
#define RG register
#define il inline
#define iter iterator
#define Max(a,b) ((a)>(b)?(a):(b))
#define Min(a,b) ((a)<(b)?(a):(b))
using namespace std;
const int N=500005;
int head[N],num=0,nxt[N<<1],to[N<<1],n;double p[N<<1];
double f[N],v[N];
void link(int x,int y,double z){
nxt[++num]=head[x];p[num]=z;to[num]=y;head[x]=num;}
void dfs(int x,int last){
int u;f[x]=(1-v[x]);
for(int i=head[x];i;i=nxt[i]){
u=to[i];if(u==last)continue;
dfs(u,x);
f[x]*=((1-f[u])*(1-p[i])+f[u]);
}
}
void dfs1(int x,int last){
int u;double t,tmp;
for(int i=head[x];i;i=nxt[i]){
u=to[i];if(u==last)continue;
t=(1-f[u])*(1-p[i])+f[u];
tmp=f[x]/t;
f[u]*=((1-tmp)*(1-p[i])+tmp);dfs1(u,x);
}
}
void work()
{
int x,y,z;
scanf("%d",&n);
for(int i=1;i<n;i++){
scanf("%d%d%d",&x,&y,&z);
link(x,y,(1.0*z)/100.0);
link(y,x,(1.0*z)/100.0);
}
for(int i=1;i<=n;i++)scanf("%lf",&v[i]),v[i]/=100.0;
dfs(1,1);dfs1(1,1);
double ans=0;
for(int i=1;i<=n;i++)ans+=1.0-f[i];
printf("%.6lf\n",ans);
}
int main(){work();return 0;}