BZOJ 3566: [SHOI2014]概率充电器 [树形DP 概率]

3566: [SHOI2014]概率充电器

题意:一棵树,每个点\(q[i]\)的概率直接充电,每条边\(p[i]\)的概率导电,电可以沿边传递使其他点间接充电。求进入充电状态的点期望个数


糖教题解传送门

每个充电的点贡献1,就是求每个点充电的概率的和

考虑树形DP ,分别求子树内的影响和父亲的影响

\(g[i]\)表示i被子树i里的点充电的概率,\(f[i]\)表示i被充电的概率

因为被子树充电时子树里的点不可能被i充电,

\[g[i] = q_i \bigcup g_v : (i,v) \in E \]

\[f[i] = P(fa被除了i之外的点充电) * p[(fa,i)] \bigcup g[i] \]

因为事件互相独立,可以使用\(P(A+B) = P(A) + P(B) -P(A)P(B)\)

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
using namespace std;
typedef unsigned long long ll;
const int N=5e5+5;
const double eps=1e-8;
inline int read() {
	char c=getchar(); int x=0, f=1;
	while(c<'0' || c>'9') {if(c=='-')f=-1; c=getchar();}
	while(c>='0' && c<='9') {x=x*10+c-'0'; c=getchar();}
	return x*f;
}

int n, m, s, val[N], u, v; double p, q[N];
struct edge{int v, ne; double p;}e[N<<1];
int cnt=1, h[N], de[N];
inline void ins(int u, int v, double p) { 
	e[++cnt]=(edge){v, h[u], p}; h[u]=cnt; 
	e[++cnt]=(edge){u, h[v], p}; h[v]=cnt; 
}
double g[N], f[N], ans;
inline double merge(double x, double y) {return x + y - x*y;}
inline double split(double x, double y) {return abs(1-y)<eps ? 1 : (x-y)/(1-y);}
void dfs1(int u, int fa) {
	for(int i=h[u];i;i=e[i].ne) if(e[i].v != fa) 
		dfs1(e[i].v, u), g[u] = merge(g[u], g[e[i].v] * e[i].p);
}
void dfs2(int u, int fa) {
	ans += f[u];
	for(int i=h[u];i;i=e[i].ne) if(e[i].v != fa) 
		f[e[i].v] = merge(g[e[i].v], split(f[u], g[e[i].v] * e[i].p) * e[i].p), dfs2(e[i].v, u);
}

int main() {
	freopen("in","r",stdin);
	n=read();
	for(int i=1; i<n; i++) u=read(), v=read(), scanf("%lf",&p), p/=100, ins(u, v, p);
	for(int i=1; i<=n; i++) scanf("%lf", &q[i]), q[i]/=100;
	dfs1(1, 0);
	f[1]=g[1];
	dfs2(1, 0);
	printf("%.6lf", ans);
}


posted @ 2017-04-02 23:42  Candy?  阅读(273)  评论(0编辑  收藏  举报