[SHOI2014]概率充电器

时间:2022-02-28 22:20:08

题目描述

著名的电子产品品牌SHOI 刚刚发布了引领世界潮流的下一代电子产品—— 概率充电器:

“采用全新纳米级加工技术,实现元件与导线能否通电完全由真随机数决 定!SHOI 概率充电器,您生活不可或缺的必需品!能充上电吗?现在就试试看 吧!”

SHOI 概率充电器由n-1 条导线连通了n 个充电元件。进行充电时,每条导 线是否可以导电以概率决定,每一个充电元件自身是否直接进行充电也由概率 决定。随后电能可以从直接充电的元件经过通电的导线使得其他充电元件进行 间接充电。

作为SHOI 公司的忠实客户,你无法抑制自己购买SHOI 产品的冲动。在排 了一个星期的长队之后终于入手了最新型号的SHOI 概率充电器。你迫不及待 地将SHOI 概率充电器插入电源——这时你突然想知道,进入充电状态的元件 个数的期望是多少呢?

输入输出格式

输入格式:

第一行一个整数:n。概率充电器的充电元件个数。充电元件由1-n 编号。

之后的n-1 行每行三个整数a, b, p,描述了一根导线连接了编号为a 和b 的 充电元件,通电概率为p%。

第n+2 行n 个整数:qi。表示i 号元件直接充电的概率为qi%。

输出格式:

输出一行一个实数,为能进入充电状态的元件个数的期望,四舍五入到小 数点后6 位小数。

输入输出样例

输入样例#1:

3
1 2 50
1 3 50
50 0 0

输出样例#1:

1.000000

输入样例#2:

5
1 2 90
1 3 80
1 4 70
1 5 60
100 10 20 30 40

输出样例#2:

4.300000

说明

对于30%的数据,n≤5000。

对于100%的数据,n≤500000,0≤p,qi≤100。

题解

概率期望 + 树形DP

题目就是求\(\sum_{i=1}^{n}{P[i]}\)

我们发现一个点可以被它的子树更新,也可以被他的父亲更新

所以我们设\(f[i]\)表示点i的子树没有点亮点i的概率

\(g[i]\)表示除了点i的子树的点没有点亮点i的概率

那么一个点没有被更新只能是 : 自己没有点亮 且 与他相连的点没有点亮 或 与他相连的点点亮了但是路断了

所以自己没有点亮是条件,而后两个是互斥的

所以后两个是相加然后与条件一相乘

所以\(f[u] = (1-p[u]) * (f[v] + (1-f[v]) * (1 - p_{u,v}))\)

然后\(g[]\)稍微难求一点

\(x = g[u] * f[u] / (f[v] + (1-f[v]) *(1 - p_{u,v}))\)

\(x表示点v是ta的父亲在不亮的情况下v不亮的情况\)

另一种情况就是ta的父亲亮了但是路断了

所以\(g[v] = x + (1-x)*(1-p_{u,v})\)

最后答案就是\(\sum{1-f[i]*g[i]}\)

代码

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
const int M = 500005 ;
using namespace std ;
inline int read() {
    char c = getchar() ; int x = 0 , w = 1 ;
    while(c>'9'||c<'0') { if(c=='-') w = -1 ; c = getchar() ; }
    while(c>='0'&&c<='9') { x = x*10+c-'0' ; c = getchar() ; }
    return x*w ;
}

int n ;
int hea[M] , num ;
double p[M] , f[M] , g[M] , Ans ;
struct E {
    int Nxt , to ; double dis ;
} edge[M << 1] ;

inline void add_edge(int from , int to , double dis) {
    edge[++num].Nxt = hea[from] ; edge[num].to = to ;
    edge[num].dis = dis ; hea[from] = num ;
}
void Dfs1(int u , int father) {
    f[u] = 1 - p[u] ;
    for(int i = hea[u] ; i ; i = edge[i].Nxt) {
        int v = edge[i].to ;
        if(v == father) continue ;
        Dfs1(v , u) ;
        f[u] *= (f[v] + (1 - f[v]) * (1 - edge[i].dis)) ;
    }
}
void Dfs2(int u , int father) {
    for(int i = hea[u] ; i ; i = edge[i].Nxt) {
        int v = edge[i].to ;
        if(v == father) continue ;
        double x = g[u] * f[u] / (f[v] + (1 - f[v]) * (1 - edge[i].dis)) ;
        g[v] = x + (1 - x) * (1 - edge[i].dis) ;
        Dfs2(v , u) ;
    }
}
int main() {
    n = read() ;
    for(int i = 1 , u , v ; i < n ; i ++) {
        u = read() ; v = read() ; double w = read() / 100.0 ;
        add_edge(u , v , w) ; add_edge(v , u , w) ;
    }
    for(int i = 1 ; i <= n ; i ++) p[i] = read() / 100.0 ;
    Dfs1(1 , 1) ; g[1] = 1 ; Dfs2(1 , 1) ;
    for(int i = 1 ; i <= n ; i ++) Ans += 1 - f[i] * g[i] ;
    printf("%.6lf\n",Ans) ;
    return 0 ;
}