[BZOJ3566] [SHOI2014]概率充电器
Description
著名的电子产品品牌 SHOI 刚刚发布了引领世界潮流的下一代电子产品——概率充电器:
“采用全新纳米级加工技术,实现元件与导线能否通电完全由真随机数决定!SHOI 概率充电器,您生活不可或缺的必需品!能充上电吗?现在就试试看吧!
”
SHOI 概率充电器由 n-1 条导线连通了 n 个充电元件。进行充电时,每条导线是否可以导电以概率决定,每一个充电元件自身是否直接进行充电也由概率决定。
随后电能可以从直接充电的元件经过通电的导线使得其他充电元件进行间接充电。
作为 SHOI 公司的忠实客户,你无法抑制自己购买 SHOI 产品的冲动。在排了一个星期的长队之后终于入手了最新型号的 SHOI 概率充电器。
你迫不及待地将 SHOI 概率充电器插入电源——这时你突然想知道,进入充电状态的元件个数的期望是多少呢?
Input
第一行一个整数:n。概率充电器的充电元件个数。充电元件由 1-n 编号。
之后的 n-1 行每行三个整数 a, b, p,描述了一根导线连接了编号为 a 和 b 的
充电元件,通电概率为 p%。
第 n+2 行 n 个整数:qi。表示 i 号元件直接充电的概率为 qi%。
Output
输出一行一个实数,为进入充电状态的元件个数的期望,四舍五入到六位小数
Sample Input
1 2 50
1 3 50
50 0 0
Sample Output
HINT
对于 100%的数据,n≤500000,0≤p,qi≤100。
较难的树形DP?
对于一个元件$x$,我们需要知道两个值,在它的子树内部把它点亮的概率(包括自己直接充电),和在它的子树外的点把它点亮的概率。
我们设$F_i$表示$x$子树内部被点亮的概率, $G_i$表示$x$子树外部的点把它点亮的概率。
对于$F_i$,我们需要一遍$dfs$,对于$G_i$我们需要第二遍$dfs$,下面说说各自怎么转移。
考虑$F_i$的转移,一个点被子树充电,是 1-它的所有儿子不给他充电的概率*自己不直接充电的概率 .
表达式就是$\large F_x=1-\left ( 1-p_x \right ) \times \prod_{y\in son_x}^{ } \left ( 1 - F_y \times w_{x,y} \right )$.
其中$w_{x,y}$表示边$ \left (x,y \right) $ 可以导电的概率。
这样$F_i$可以得到,下面说如何得到$G_i$.
还是考虑除子树外的点可以给他充电的概率可以转化成 $x$父亲节点获得电的概率*$\large w_{fa,x}$.
那么$x$的父亲节点获得电的概率如何算呢?
$\large 1 - \left (1-F_{fa} \right) * \left (1-G_{fa} \right)$。
但是这样算是不对的,因为我们不能考虑$x$这棵子树给$fa$的贡献, 所以必须从$F_{fa}$中把来自$x$子树的贡献去掉。
经过对$F_i$的递推公式的变化和计算我们得到 $\large 1-F_{fa}$
可以转化为 $\large \frac{1-F_{fa}}{1-F_x \times w_{fa,x}}$
这样我们$G_i$的递推式就是
$\large \left ( 1-\frac{1-F_{fa}}{1-F_x \times w_{fa,x}} \times \left ( 1-G_{fa} \right ) \right ) \times w_{fa,x}$。
#include <iostream> #include <cstdio> #include <algorithm> #include <cstring> using namespace std; #define reg register inline int read() { int res=0;char ch=getchar();bool fu=0; while(!isdigit(ch)){if(ch=='-')fu=1;ch=getchar();} while(isdigit(ch))res=(res<<3)+(res<<1)+(ch^48), ch=getchar(); return fu?-res:res; } #define N 500005 int n; struct edge { int nxt, to; double w; }ed[N*2]; int head[N], cnt; inline void add(int x, int y, double z) { ed[++cnt] = (edge){head[x], y, z}; head[x] = cnt; } double p[N]; double f[N], g[N]; double ans; void dp1(int x, int fa) { double sum = 1.0; for (reg int i = head[x] ; i ; i = ed[i].nxt) { int to = ed[i].to; if (to == fa) continue; dp1(to, x); sum *= (1.0 - f[to] * ed[i].w); } f[x] = 1.0 - (1.0 - p[x]) * sum; } void dp2(int x, int fa, int in) { if (1.0 - f[x] * ed[in].w > 1e-9) g[x] = (double)ed[in].w * (1.0 - ((1.0 - f[fa]) / (1.0 - f[x] * ed[in].w)) * ((1.0 - g[fa]))); for (reg int i = head[x] ; i ; i = ed[i].nxt) { int to = ed[i].to; if (to == fa) continue; dp2(to, x, i); } } int main() { n = read(); for (reg int i = 1 ; i < n ; ++ i) { int x = read(), y = read(), z = read(); add(x, y, (double)z * 0.01), add(y, x, (double)z * 0.01); } for (reg int i = 1 ; i <= n ; ++ i) { int x = read(); p[i] = (double)x * 0.01; } dp1(1, 0); dp2(1, 0, 0); for (reg int i = 1 ; i <= n ; ++ i) ans += 1 - (1.0 - f[i]) * (1.0 - g[i]); printf("%.6lf\n", ans); return 0; }