题解: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;
}
posted @ 2026-09-18 07:56  wmq2012  阅读(4)  评论(0)    收藏  举报