题解:P4284 [SHOI2014] 概率充电器
笔记列表
- 一眼树形 \(dp\)
- 求通电的个数就等于是求每一个通电的概率求和
- 设 \(f_i\) 表示 \(i\) 通电的概率
- 好的,答案就是 \(f\) 求和
- 初始化就是 \(f_i = p_i\) , \(i\) 自己给自己通电
- 先不考虑 \(u\) 的父亲方向来电使得 \(u\) 通电的情况,先只考虑 \(u\) 和 \(u\) 子树来电使得 \(u\) 通电的情况
- 简单转移 对于 \(v\in son(u)\)
\[f_u = f_u + (1-f_u)\times f_v \times w(u,v)
\]
-
很好理解,就是 \(u\) 已经来电和 \(u\) 没有电且 \(v\) 来电
-
接下来考虑从父亲转移到儿子,也就是儿子自己没有电,而父亲有电,通过去,使得儿子有电的情况
-
直接转移会出现一个问题
-
如果 \(f_u\) 是从 \(f_v\) 转移过来的, 那么这个时候不能在用\(f_u\) 去转移 \(f_v\)
-
也就是在用 \(f_u\) 转移 \(f_v\) 的时候要把 \(f_v\) 原来给 \(f_u\) 的贡献先临时去除
-
得到 \(u\) 在不接受 \(v\) 子树方向来电情况下有电的概率,在用这个去转移 \(f_v\)
-
如何计算
-
设这个我们要得到的 \(g_u\) 表示 \(u\) 来电,且 \(u\) 来的这个电不是来自 \(v\) 的概率
\[f_u = g_u + (1-g_u) \times f_v\times w(u,v)
\]
-
好的这个等式很好理解,就是 \(u\) 来电的概率是除了 \(v\) 来电的概率,和除了 \(v\) 之后没有来电且 \(v\) 来电了的概率
-
把这个等式变形
\[g_u = \frac{f_u - f_v \times w(u, v)}{1 - f_v \times w(u,v)}
\]
这个时候在用 \(g_u\) 来更新 \(f_v\)
\[f_v = f_v + (1-f_v)\times g_u \times w(u,v)
\]
#include<bits/stdc++.h>
#define int long long
#define fore(i,a,b) for(int i=(a); i<=(b); ++i)
using namespace std;
const int N = 5e5 + 10;
const double eps = 1e-7;
struct edge {
int v;
double w;
};
vector<edge> G[N];
int n;
double f[N], a[N];
void dfs(int u, int fa) {
f[u] = a[u];
for (auto e : G[u]) {
int v = e.v;
double w = e.w;
if (v == fa) continue;
dfs(v, u);
f[u] = f[u] + f[v] * (1.0 - f[u]) * w;
}
}
void dfs2(int u, int fa) {
for (auto e : G[u]) {
int v = e.v;
double w = e.w;
if (v == fa) continue;
double p = 1.0 - f[v] * w;
if (p > eps) {
double from_u = (f[u] - f[v] * w) / p;
// 代码这里的from_u就是g[u],注意这个地方要判断是否除0
f[v] += (1.0 - f[v]) * from_u * w;
}
dfs2(v, u);
}
}
signed main() {
ios::sync_with_stdio(false);
cin.tie(0); cout.tie(0);
cin >> n;
fore(i, 2, n) {
int u, v, ww;
cin >> u >> v >> ww;
double w = ww * 1.0 / 100.0;
G[u].push_back({v, w});
G[v].push_back({u, w});
}
fore(i, 1, n) {
int w;
cin >> w;
a[i] = w * 1.0 / 100.0;
}
dfs(1, 0);
dfs2(1, 0);
double ans = 0;
fore(i, 1, n) ans += f[i];
cout << fixed << setprecision(6) << ans << '\n';
return 0;
}

浙公网安备 33010602011771号