Charger / 概率充电器
题目链接:jzoj 3661 / luogu P4284
题目大意
有一些点,一个点有亮不亮两种可能。
一个点本身有一定的概率会亮,然后点之间有边,把点连接构成了一棵树。
然后每条边有一定概率,使得如果两边有一个点亮了,另一边也会亮。
问你亮的点的期望个数。
思路
你可以求出每个点亮的概率,然后加起来就是期望。
你考虑求一个
x
x
x 点亮的概率,发现可以 DP 求,设
f
i
f_i
fi 为
i
i
i 亮的概率。(只考虑以
x
x
x 为根它的子树)
然后就 DP,
f
i
=
q
i
∏
j
=
s
o
n
i
f
j
p
i
,
j
f_i=q_i\prod\limits_{j=son_i}f_jp_{i,j}
fi=qij=soni∏fjpi,j。
然后复杂度是
O
(
n
2
)
O(n^2)
O(n2),然后你考虑换根。
然后你发现这么设换根不太好搞,我们考虑设不亮的概率。
f
i
=
(
1
−
q
i
)
∗
∏
j
=
s
o
n
i
(
1.0
−
p
i
,
j
+
p
i
,
j
f
j
)
f_i=(1-q_i)*\prod\limits_{j=son_i}(1.0-p_{i,j}+p_{i,j}f_{j})
fi=(1−qi)∗j=soni∏(1.0−pi,j+pi,jfj)
然后你继续考虑换根,你发现新的答案其实就是你旧的答案减去你这个点子树的贡献再作为你这个点的子树贡献上来。
减去这个子树的贡献其实就是除以右边的那个。
(具体可以看看代码)
然后搞出来就好了。
然后因为 jzoj 用 dfs 会爆栈 RE,所以我自己写了个比较丑的 bfs。
代码
#include<queue>
#include<cstdio>
using namespace std;
struct node {
double x;
int to, nxt;
}e[2000001];
int n, x, y, z, le[1000001], KK;
double p[1000001], f[1000001], ans, g[1000001];
int st1[10000001], st2[10000001], st3[10000001];
void add(int x, int y, double z) {
e[++KK] = (node){z, y, le[x]}; le[x] = KK;
e[++KK] = (node){z, x, le[y]}; le[y] = KK;
}
//求出一个为根的结果
void bfs(int now, int father) {
st1[++st1[0]] = now;
st2[++st2[0]] = father;
st3[++st3[0]] = 0;
int op;
while (st1[0]) {
now = st1[st1[0]--];
father = st2[st2[0]--];
op = st3[st3[0]--];
if (!op) {
st1[++st1[0]] = now;
st2[++st2[0]] = father;
st3[++st3[0]] = 1;
f[now] = 1.0 - p[now];
for (int i = le[now]; i; i = e[i].nxt)
if (e[i].to != father) {
st1[++st1[0]] = e[i].to;
st2[++st2[0]] = now;
st3[++st3[0]] = 0;
// q1.push(e[i].to); q2.push(now); q3.push(0);
// f[now] *= (1.0 - e[i].x + e[i].x * f[e[i].to]);
}
// q1.push(now); q2.push(father); q3.push(1);
}
else {
for (int i = le[now]; i; i = e[i].nxt)
if (e[i].to != father) {
f[now] *= (1.0 - e[i].x + e[i].x * f[e[i].to]);
}
}
}
}
//换根DP
void bfsDio(int now, int father) {
queue <int> q1, q2;
q1.push(now); q2.push(father);
while (!q1.empty()) {
now = q1.front(); q1.pop();
father = q2.front(); q2.pop();
ans += g[now];
for (int i = le[now]; i; i = e[i].nxt)
if (e[i].to != father) {
double el = g[now] / (1.0 - e[i].x + e[i].x * f[e[i].to]);
g[e[i].to] = f[e[i].to] * (1.0 - e[i].x + e[i].x * el);
q1.push(e[i].to); q2.push(now);
}
}
}
int main() {
// freopen("charger.in", "r", stdin);
// freopen("charger.out", "w", stdout);
scanf("%d", &n);
for (int i = 1; i < n; i++) {
scanf("%d %d %d", &x, &y, &z);
add(x, y, 1.0 * z / 100.0);
}
for (int i = 1; i <= n; i++) {
scanf("%d", &x);
f[i] = p[i] = 1.0 * x / 100.0;
}
bfs(1, 0);
g[1] = f[1];
bfsDio(1, 0);
printf("%.6lf", 1.0 * n - ans);
fclose(stdin);
fclose(stdout);
return 0;
}