1. 程式人生 > >【BZOJ】3566: [SHOI2014]概率充電器

【BZOJ】3566: [SHOI2014]概率充電器

algorithm blog gif 表示 引用 clu oid 必須 scanf

【算法】樹型DP+期望DP

【題意】一棵樹上每個點均有直接充電概率qi%,每條邊有導電概率pi%,問期望有多少結點處於充電狀態?

【題解】引用自:【BZOJ3566】【SHOI2014】概率充電器 樹形DP 概率DP by 空灰冰魂

最大的難點在於計算每個點充電期望時,兩個節點各自的期望都會影響對方的期望。

所以考慮轉化對象,改為求每個節點充不上電的期望,充不上電就不用考慮兩者的相互影響。

fi表示結點i由子結點和自身充不上電的概率

gi表示結點i由父結點充不上電的概率

第一次DFS

hi表示結點i對父親貢獻的概率

fi=(1-qi)*∏h[son[i]]

hi=fi+(1-fi)*(1-pi)  pi為i到父親的導線通電概率

☆兩者發生其一用概率加法,多者都必須發生用概率乘法,P(A+B)=P(A)+P(B)-P(AB)註意去除交集。

第二次DFS

當前結點x,父親結點y。

t表示父親y對結點x的貢獻。

t=gy*(fy/hx)  註意hx為0的情況(除0)

gx=t+(1-t)*(1-pi)  pi為x到y的導線概率

最終答案:ans=Σ(1-fi*gi)  因為概率和期望都是0~1,所以一樣。

技術分享
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn=500010
; struct edge{int v,from;double p;}e[maxn*2]; int n,first[maxn],tot; double q[maxn],f[maxn],g[maxn],h[maxn],fw[maxn]; void insert(int u,int v,double w) {tot++;e[tot].v=v;e[tot].p=w;e[tot].from=first[u];first[u]=tot;} void dfs1(int x,int fa) { f[x]=1-q[x]; for(int i=first[x];i;i=e[i].from)if(e[i].v!=fa) { dfs1(e[i].v,x); f[x]
*=h[e[i].v]; }else fw[x]=e[i].p; h[x]=f[x]+(1-f[x])*(1-fw[x]); } void dfs2(int x,int y) { double t; if(!h[x])t=0;else t=g[y]*f[y]/h[x]; g[x]=t+(1-t)*(1-fw[x]); for(int i=first[x];i;i=e[i].from)if(e[i].v!=y)dfs2(e[i].v,x); } int main() { scanf("%d",&n); for(int i=1;i<n;i++) { int u,v,w; scanf("%d%d%d",&u,&v,&w); insert(u,v,1.0*w/100); insert(v,u,1.0*w/100); } for(int i=1;i<=n;i++){int u;scanf("%d",&u);q[i]=1.0*u/100;} dfs1(1,0); dfs2(1,0); double ans=0; for(int i=1;i<=n;i++)ans+=1-f[i]*g[i]; printf("%.6lf",ans); return 0; }
View Code

【BZOJ】3566: [SHOI2014]概率充電器