Bzoj3566: [SHOI2014]概率充电器
题面
Sol
算出每个点从子树内使它没电的概率和子树外使它没电的概率即可
注意算子树外使它没电的概率时,父亲转移来要除掉它的贡献,直接除可能有\(0\)的情况
可以把每个点的儿子排成一列,求一遍前后缀的积来计算
# include <bits/stdc++.h>
# define RG register
# define IL inline
# define Fill(a, b) memset(a, b, sizeof(a))
using namespace std;
typedef long long ll;
const int _(5e5 + 5);
IL int Input(){
RG int x = 0, z = 1; RG char c = getchar();
for(; c < '0' || c > '9'; c = getchar()) z = c == '-' ? -1 : 1;
for(; c >= '0' && c <= '9'; c = getchar()) x = (x << 1) + (x << 3) + (c ^ 48);
return x * z;
}
double ans, f[_], g[_], tmp[_], tp[_], h[_], p[_];
int n, cnt, first[_], son[_], len;
struct Edge{
int to, next;
double p;
} edge[_ << 1];
IL void Add(RG int u, RG int v, RG double pi){
edge[cnt] = (Edge){v, first[u], pi}, first[u] = cnt++;
edge[cnt] = (Edge){u, first[v], pi}, first[v] = cnt++;
}
IL void Dfs1(RG int u, RG int ff){
for(RG int e = first[u]; e != -1; e = edge[e].next){
RG int v = edge[e].to;
if(v == ff) continue;
Dfs1(v, u);
tp[v] = edge[e].p;
h[v] = f[v] + (1.0 - f[v]) * (1.0 - tp[v]);
f[u] *= h[v];
}
}
IL void Dfs2(RG int u, RG int ff){
len = 0;
for(RG int e = first[u]; e != -1; e = edge[e].next)
if(edge[e].to != ff) son[++len] = edge[e].to;
RG double t = 1.0, ft = g[u] * (1.0 - p[u]); tmp[len + 1] = 1.0;
for(RG int i = len; i; --i) tmp[i] = tmp[i + 1] * h[son[i]];
for(RG int i = 1; i <= len; ++i){
RG double pi = t * ft * tmp[i + 1];
g[son[i]] = pi + (1.0 - pi) * (1.0 - tp[son[i]]);
t *= h[son[i]];
}
for(RG int e = first[u]; e != -1; e = edge[e].next)
if(edge[e].to != ff) Dfs2(edge[e].to, u);
}
int main(RG int argc, RG char *argv[]){
Fill(first, -1);
n = Input();
for(RG int i = 1; i < n; ++i){
RG int u = Input(), v = Input(), pi = Input();
Add(u, v, 0.01 * pi);
}
for(RG int i = 1; i <= n; ++i) p[i] = 0.01 * Input(), f[i] = 1.0 - p[i];
Dfs1(1, 0), g[1] = 1, Dfs2(1, 0);
for(RG int i = 1; i <= n; ++i) ans += 1.0 - f[i] * g[i];
printf("%.6lf\n", ans);
return 0;
}