1. 程式人生 > 其它 >[SHOI2014]概率充電器

[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](h[j]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;
}