这题要算期望,个数的期望
容易发现每一个点对期望的贡献就是它被接通的概率
之后考虑 dp
在玩方程的时候会发现这样一个事情:如果状态定义为被接通的概率的话,在转移时要加子树的然后再除个总概率什么的,总之很麻烦
但是可以考虑一下计算不被接通的概率,这样计算一个 pi 就好了
之后容易想到二次扫描换根
转移子树的方程是 : d[x] = (1 - p[x]) * ∏[d[son[i]] * edge[i].val + 1 - edge[i].val]
转移以当前点为根的方程是 : f[x] = d[x] * {(1 - edge[i].val) +edge[i].val * [f[fa] / (d[x] *edge[i].val + 1 - edge[i].val)]}
需要注意的是这样定义状态会导致转移方程中出现除,并且这个除数是一个带减号的式子,所以可能会除 0,判掉就好
代码:
#include<algorithm> #include<iostream> #include<cstdlib> #include<cstring> #include<cctype> #include<cstdio> using namespace std; const int MAXN = 500005; const double eps = 1e-6; struct EDGE{ int nxt, to; double val; EDGE(int NXT = 0, int TO = 0, double VAL = 0.0) {nxt = NXT; to = TO; val = VAL;} }edge[MAXN << 1]; int n, totedge; int head[MAXN]; double d[MAXN], f[MAXN], p[MAXN]; double ans; inline void add(int x, int y, double v) { edge[++totedge] = EDGE(head[x], y, v); head[x] = totedge; return; } void dfs(int x, int fa) { d[x] = 1.0 - p[x]; for(int i = head[x]; i; i = edge[i].nxt) if(edge[i].to != fa) { int y = edge[i].to; dfs(y, x); d[x] *= ((1.0 - edge[i].val) + (edge[i].val * d[y])); } return; } void efs(int x, int fa) { for(int i = head[x]; i; i = edge[i].nxt) if(edge[i].to != fa) { int y = edge[i].to; double k = (d[y] * edge[i].val + 1.0 - edge[i].val); if(k > eps) f[y] = d[y] * ((1.0 - edge[i].val) + edge[i].val * f[x] / k); efs(y, x); } return; } int main() { scanf("%d", &n); register int xx, yy; register double vv; for(int i = 1; i < n; ++i) { scanf("%d%d%lf", &xx, &yy, &vv); vv /= 100.0; add(xx, yy, vv); add(yy, xx, vv); } for(int i = 1; i <= n; ++i) { scanf("%lf", &p[i]); p[i] /= 100.0; } dfs(1, 0); f[1] = d[1]; efs(1, 0); for(int i = 1; i <= n; ++i) ans += 1.0 - f[i]; printf("%lf ", ans); return 0; }