bzoj 3566: [SHOI2014]概率充电器 数学期望+换根dp

时间:2022-09-11 04:48:59

题意:给定一颗树,树上每个点通电概率为 $q[i]$%,每条边通电的概率为 $p[i]$%,求期望充入电的点的个数.

期望在任何时候都具有线性性,所以可以分别求每个点通电的概率(这种情况下期望=概率 $\times 1$ )然后累加.

然而,直接求通电的概率不是很好求,所以可以求不通电的概率,然后 $1$ 减去这个就是通电的概率了~

先假定以 $1$ 为根,令 $f[i]$ 表示仅考虑 $i$ 的子树及 $i$ 的影响时 $i$ 充不到电的概率.

则有: $f[i]=(1-q[i])\prod_{e\in(u,v)}(1-p[i]+p[i]f[i])$

而在 $i=1$ 时,$f[i]$ 就是 $1$ 号点不通电的概率,但是对于其余所有点,我们还需考虑父亲的影响:考虑换根dp

令 $g[i]$ 表示 $i$ 点的最终答案,即考虑所有情况下 $i$ 点不通电的概率.

那么,当 $i=1$ 时,$g[1]=f[1]$

而 $i\neq1$ 时,令 $p$ 表示只考虑父亲对 $i$ 的影响,而 $p=\frac{g[fa]}{1-p[e]+p[e]f[i]}$ $e$ 为 $i$ 到 $fa$ 的边.

则有 $g[i]=f[i]\times (1-p[e]+p[e]\times p)$

求完之后再累加一下即可.

#include <bits/stdc++.h>
#define N 500002
#define LL long long
#define setIO(s) freopen(s".in","r",stdin)
using namespace std;
int n,edges;
double f[N],g[N],perc[N<<1],q[N];
int hd[N],to[N<<1],nex[N<<1];
void add(int u,int v,double c)
{
nex[++edges]=hd[u],hd[u]=edges,to[edges]=v,perc[edges]=c;
}
void dfs1(int u,int ff)
{
f[u]=1-q[u];
for(int i=hd[u];i;i=nex[i])
{
int v=to[i];
if(v==ff) continue;
dfs1(v,u);
f[u]*=(1-perc[i]+perc[i]*f[v]);
}
}
void dfs2(int u,int ff,int j)
{
if(u==1) g[u]=f[u];
else
{
double p=g[ff]/(1-perc[j]+perc[j]*f[u]);
g[u]=f[u]*(1-perc[j]+perc[j]*p);
}
for(int i=hd[u];i;i=nex[i])
{
int v=to[i];
if(v==ff) continue;
dfs2(v,u,i);
}
}
int main()
{
// setIO("input");
int i,j;
scanf("%d",&n);
for(i=1;i<n;++i)
{
int x,y,z;
scanf("%d%d%d",&x,&y,&z),add(x,y,(double)z/100),add(y,x,(double)z/100);
}
for(i=1;i<=n;++i) scanf("%lf",&q[i]), q[i]/=100;
dfs1(1,0);
dfs2(1,0,0);
double ans=0;
for(i=1;i<=n;++i) ans+=1.0-g[i];
printf("%.6lf",ans);
return 0;
}