题解SHOI2014概率充电器
Posted twilight-sx
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了题解SHOI2014概率充电器相关的知识,希望对你有一定的参考价值。
首先发现答案就是每个节点有电的概率之和。有电的概率牵扯太广不好求,所以转化为求没有电的概率。这题最难的部分在于:一个节点如果有电,可以来自儿子,也可以来自父亲。我们考虑将这两个部分分离开来:建立状态 (g[i]) 和 (f[i]) 分别表示一个节点不由子树节点供电的概率以及不由父亲节点供电的概率。
(g[u] = prod (g[v] + (1 - g[v]) * (1 - w(u, v))]))
其中(v) 为 (u) 的子节点,(w(u, v)) 为 (u, v) 边有电的概率
利用这个递推式我们可以dfs一遍自下而上获取所有节点的 (g[u]);
然后考虑如何求得 (f[u])。要注意由于 (f[u]) 是 (u) 的父亲不供电给 (u) 的概率,所以在利用父亲的信息时应该要除去儿子的影响:
父亲 (F) 没有电的概率 (P = (1 - p[F]) * f[F] * frac{g[F]}{g[u] + (1 - g[u]) * w(F, u))} )
父亲不供电给儿子的概率为 :
(f[u] = P + (1 - P) * (1 - w(F, u)))
这样就解决啦~
#include <bits/stdc++.h> using namespace std; #define maxn 505000 #define db double #define eps 0.0000001 int n, cnp = 1; int head[maxn]; db ans, p[maxn], f[maxn], g[maxn]; struct edge { int to, last; db co; }E[maxn * 2]; void add(int u, int v, db w) { E[cnp].to = v, E[cnp].co = w; E[cnp].last = head[u], head[u] = cnp ++; } bool check(db x) { return abs(x - 0.0) < eps; } void dfs(int u, int fa) { f[u] = 1.0; for(int i = head[u]; i; i = E[i].last) { int v = E[i].to; if(v == fa) continue; dfs(v, u); f[u] *= f[v] + (1.0 - f[v]) * (1.0 - E[i].co); } f[u] *= (1.0 - p[u]); } void dfs2(int u, int fa) { for(int i = head[u]; i; i = E[i].last) { int v = E[i].to; if(v == fa) continue; db P = g[u] * f[u] / (f[v] + (1.0 - f[v]) * (1.0 - E[i].co)); g[v] = P + (1.0 - P) * (1.0 - E[i].co); dfs2(v, u); } ans += 1.0 - (g[u] * f[u]); } int main() { scanf("%d", &n); for(int i = 1; i < n; i ++) { int a, b, p; scanf("%d%d%d", &a, &b, &p); add(a, b, (db) p / 100.0); add(b, a, (db) p / 100.0); } for(int i = 1; i <= n; i ++) scanf("%lf", &p[i]), p[i] /= 100.0; g[1] = 1.0; dfs(1, 0); dfs2(1, 0); printf("%.6lf ", ans); return 0; }
以上是关于题解SHOI2014概率充电器的主要内容,如果未能解决你的问题,请参考以下文章