Time:2016.08.25
Author:xiaoyimi
转载注明出处谢谢
传送门
思路:
一开始我连暴力都打不对
答案就是
∑Pi
i=1..n
P表示每个点被通电的概率
如果把题目中的树转化成有根树,那么这个充电概率P就等于
P自身通电∪P儿子通电⋅W(自身−>儿子)∪P父亲通电⋅W(父亲−>自身
前两项比较好弄,一个是题目给出的,一个可以
O(n)
遍历通过
P(A∪B)=P(A)+P(B)−P(A∩B)
计算出来的
后一项要涉及到容斥,考虑当前点为v,父亲为u,在计算父亲充电概率时的具体式子是
Pu=P′u+Pv⋅w(u,v)−P′u∗Pv⋅w(u,v)
其中 P′u 表示除v以外,u的儿子对 Pu 的贡献
再想想我们要计算什么?
父亲对儿子的贡献,即u对v的贡献
显然此时v不能有电,也就是要把 Pv 的贡献去掉,用 P′u 来计算
化简上式就可以得到
P′u=Pu−Pv⋅w(u,v)1−Pv⋅w(u,v)
所以再遍历一遍就可以了
复杂度 O(n)
值得注意的是精度问题,以及如果 1−Pv⋅w(u,v)=0 那么 w(u,v)=Pv=Pu=1
那么最终概率就是1了
代码:
#include<cstdio>
#include<cmath>
#define M 500001
using namespace std;
int n,tot=1,x,y;
double z;
int first[M];
double f[M];
int in()
{
char ch=getchar();int t=0;
while (ch<'0'||ch>'9') ch=getchar();
while (ch>='0'&&ch<='9') t=(t<<3)+(t<<1)+ch-48,ch=getchar();
return t;
}
struct edge{
int u,v,next;
double w;
}e[M<<1];
void add(int x,int y,double z)
{
e[++tot]=(edge){x,y,first[x],z};first[x]=tot;
e[++tot]=(edge){y,x,first[y],z};first[y]=tot;
}
void dfs1(int x,int fa)
{
for (int i=first[x];i;i=e[i].next)
if (e[i].v!=fa)
dfs1(e[i].v,x),
f[x]=f[x]+f[e[i].v]*e[i].w-f[x]*f[e[i].v]*e[i].w;
}
void dfs2(int x,int fa)
{
double tt;
for (int i=first[x];i;i=e[i].next)
if (e[i].v!=fa)
{
if (fabs(1-e[i].w*f[e[i].v])>1e-6)
tt=(f[x]-f[e[i].v]*e[i].w)/(1-e[i].w*f[e[i].v]),
f[e[i].v]=f[e[i].v]+tt*e[i].w-f[e[i].v]*tt*e[i].w;
else f[e[i].v]=1;
dfs2(e[i].v,x);
}
}
main()
{
n=in();
for (int i=1;i<n;++i)
x=in(),y=in(),z=in()/100.0,
add(x,y,z);
for (int i=1;i<=n;i++) f[i]=in()/100.0;
dfs1(1,0);
dfs2(1,0);
for (int i=1;i<=n;i++) f[0]+=f[i];
printf("%lf",f[0]);
}