●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;
}

 

  

 

posted @ 2018-03-11 08:45  *ZJ  阅读(126)  评论(0编辑  收藏  举报