●BZOJ 3566 [SHOI2014]概率充电器
题链:
http://www.lydsy.com/JudgeOnline/problem.php?id=3566
题解:
概率dp,树形dp
如果求出每个点被通电的概率t,
那么期望答案就是t1×1+t2×1+t3*1+...+tn×1
现在问题就是要去求每个点被通电的概率。
因为是一颗树,所以每个点是否通电只由三个因素决定:
自己给自己通电;儿子给自己通电;父亲给自己通电。
这里采取求反面的方法:
对于每个点u,
1.求出u所在的子树不能给u点通电的概率f[u]。
2.求出u的父亲不能给u点通电的概率g[u]。
那么最终,每个点可以被通电的概率就是1-f[u]*g[u].
对于f[u]的求法:
dfs这颗树,用儿子v去更新父亲节点u:
$$f[u]=(1-q[u])\times \prod_{u->v:p(边的概率为p)}(f[v]+(1-f[v])*(1-p))$$
对于g[u]的求法:
同样的dfs这颗树,用父亲u去更新儿子节点v
先求出除了v之外,其他的点使得u通电的概率:t=f[u]*g[u]/(f[v]+(1-f[v])*(1-p));
(就是除掉儿子对父亲的贡献,注意(f[v]+(1-f[v])*(1-p))等于0的情况)
然后$$g[v]=t+(1-t)\times (1-p)$$
然后计算答案即可。
代码:
#include<bits/stdc++.h> #define MAXN 500005 using namespace std; const double eps=1e-9; int dcmp(double x){ if(fabs(x)<eps) return 0; return x>0?1:0; } struct Edge{ int ent; double p[MAXN*2]; int to[MAXN*2],nxt[MAXN*2],head[MAXN]; Edge(){ent=2;} void Adde(int u,int v,int w){ to[ent]=v; p[ent]=1.0*w/100; nxt[ent]=head[u]; head[u]=ent++; } }E; double f[MAXN],g[MAXN],q[MAXN],ANS; int N; void dfs1(int u,int dad){ f[u]=(1-q[u]); for(int e=E.head[u];e;e=E.nxt[e]){ int v=E.to[e]; if(v==dad) continue; dfs1(v,u); f[u]*=(f[v]+(1-f[v])*(1-E.p[e])); } } void dfs2(int u,int dad){ double t; for(int e=E.head[u];e;e=E.nxt[e]){ int v=E.to[e]; if(v==dad) continue; if(dcmp(f[v]+(1-f[v])*(1-E.p[e]))!=0) t=f[u]*g[u]/(f[v]+(1-f[v])*(1-E.p[e])); else t=0; g[v]=t+(1-t)*(1-E.p[e]); dfs2(v,u); } } int main(){ ios::sync_with_stdio(0); cin>>N; for(int i=1,a,b,c;i<N;i++) cin>>a>>b>>c,E.Adde(a,b,c),E.Adde(b,a,c); for(int i=1;i<=N;i++) cin>>q[i],q[i]/=100; g[1]=1; dfs1(1,0); dfs2(1,0); for(int i=1;i<=N;i++) ANS+=1-f[i]*g[i]; cout<<fixed<<setprecision(6)<<ANS<<endl; return 0; }
Do not go gentle into that good night.
Rage, rage against the dying of the light.
————Dylan Thomas