[SHOI2014]概率充电器

[SHOI2014]概率充电器

题意:

有一棵树,每个点都有直接通电的概率,每条线有导电的概率,一个点的电可以通过导线传递到其他点,询问通电点数的期望。

分析:

通电的点的数量的期望就是每个点通电的概率之和

先设 \(h[i]\) 作为一个点是否通电的概率,\(p[i,j]\) 为导线 \((i,j)\) 导电的概率。

初始化 \(h[i]=q[i]/100\) ,然后进行分析:

一个点通电有三种情况:

  1. 自己自身带电
  2. 通过儿子节点导电
  3. 通过自己的父亲节点导电

第一种情况好解决,但是第二种情况和第三种情况我们不好解决,因此,我们要把这两种情况变成一种,什么时候这两种可以合并?

当然是计算根节点的时候:根节点通过自己儿子带电。

考虑 \(dp\) ,设 \(h[j]\)\(i\) 的儿子带电的概率,则 \(i\) 带电的概率为:

$$h[i]=h[i]+h[j]\times p(i,j)-h[i]\times (h[j] \times p(i,j))$$

因为有期望公式: \(P(A和B至少发生一个)=P(A)+P(B)-P(A)*P(B)\)

我们依次搜索,通过儿子更新父亲的概率。

第一个搜索,我们算出了 \(i\) 本身和 \(i\) 的子树造成的 \(i\) 带电的概率。

那么,\(i\) 还有可能通过父亲节点带电,因此还需要一次 \(dp\) 更新状态。

我们设 \(i\)\(j\) 节点的父亲节点,此时 \(i\) 已经计算完成概率。

因此,\(j\) 通过父亲节点带电的概率就是:

( \(i\) 除了 \(j\) 这棵子树的带电的概率) \(\times p(i,j)\)

\(P(B)=h[j] \times p(i,j)\) 表示 \(j\) 这棵子树带电的概率, \(P(A)\) 即为除了 \(j\) 带电的概率。

因为 \(h[i]=P(A)+P(B)-P(A)P(B)\) ,所以 \(P(A)\) 算出即为:

\[P(A)=\frac{h[i]-P(B)}{1-P(B)} \]

这样,通过父亲节点带电的概率就是 \(res=P(A)*p(i,j)\)

然后, \(j\) 带电的概率,依然通过期望公式带入,得到:

\(h[j]=res+h[j]-res \times h[j]\)

把所有概率加起来即是答案。

代码:

// P4284 [SHOI2014]概率充电器
#include<bits/stdc++.h>
using namespace std;
#define db double
const int N=1e6+5;
const double eps=1e-7;
int nxt[N],ver[N],tot,head[N];db edge[N];
db ans,h[N]; 
int n,dep[N];

bool check(db x,db y){
    if(x+eps>y&&x-eps<y) return 1;
    return 0;
}

void add(int x,int y,db z){
    ver[++tot]=y; edge[tot]=z; nxt[tot]=head[x]; head[x]=tot;
}

void dfs(int x,int fa){
    dep[x]=dep[fa]+1;
    for(int i=head[x];i;i=nxt[i]){
        int y=ver[i]; db z=edge[i];
        if(y==fa) continue;
        dfs(y,x);
        double res=h[y]*z;//儿子节点能使其导电的概率
        h[x]=h[x]+res-h[x]*res;
    }
}

void dfs2(int x,int fa){
    ans+=h[x];//
    for(int i=head[x];i;i=nxt[i]){
        int y=ver[i]; db z=edge[i];
        if(y==fa) continue;
        if(check(h[y]*z,1)){dfs2(y,x); continue;}//判断当前点是不是已经必亮
        db res=(h[x]-h[y]*z)/(1-h[y]*z)*z;
        h[y]=h[y]+res-res*h[y];
        dfs2(y,x);
    }
}


int main(){
    cin>>n;
    for(int i=1,x,y;i<n;i++){
        db z; scanf("%d%d%lf",&x,&y,&z); z=z*0.01;
        add(x,y,z); add(y,x,z);
    }
    for(int i=1;i<=n;i++){
        scanf("%lf",&h[i]); h[i]=h[i]*0.01;
    }
    dfs(1,0);
    dfs2(1,0);
    printf("%.6lf\n",ans);
    system("pause");
    return 0;
}

posted @ 2021-10-09 08:58  Evitagen  阅读(38)  评论(0编辑  收藏  举报