BZOJ3566: [SHOI2014]概率充电器
这题要算期望,个数的期望
容易发现每一个点对期望的贡献就是它被接通的概率
之后考虑 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\n", ans);
return 0;
}
禁止诸如开发者知识库/布布扣/码迷/学步园/马开东等 copy 他人博文乃至博客的网站转载
,用户转载请注明出处:https://www.cnblogs.com/xcysblog/

浙公网安备 33010602011771号