[BZOJ3566][SHOI2014]概率充电器

bzoj
luogu

sol

很显然的两遍树DP吧。
\(f_i\)表示只考虑\(i\)的子树,\(i\)点没有电的概率。

\[f_i=q_i*\prod_{v是i的儿子}f_v+(1-f_v)*(1-p_e) \]

为了方便表示把后面那一坨记为\(h_v\),即\(h_i=f_i+(1-f_i)*(1-p_e)\)
然后再记一个\(g_i\)表示\(i\)的父亲不向\(i\)供电的概率,那么\(g_i=f_{fa}*g_{fa}/h_i\)
因为\(h_i\)可能等于\(0\),所以第二次DP的时候要记一下所有儿子中\(h_v=0\)的个数,判一下即可。
最终\(ans=\sum_{i=1}^{n}1-f_i*g_i\)

code

#include<cstdio>
#include<algorithm>
using namespace std;
int gi()
{
	int x=0,w=1;char ch=getchar();
	while ((ch<'0'||ch>'9')&&ch!='-') ch=getchar();
	if (ch=='-') w=0,ch=getchar();
	while (ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
	return w?x:-x;
}
const int N = 5e5+5;
int n,to[N<<1],nxt[N<<1],head[N],cnt;
double ww[N<<1],val[N],f[N],g[N],h[N],ans;
void link(int u,int v,int w)
{
	to[++cnt]=v;nxt[cnt]=head[u];ww[cnt]=(double)w/100.0;
	head[u]=cnt;
}
void dfs1(int u,int fa)
{
	f[u]=val[u];
	for (int e=head[u];e;e=nxt[e])
		if (to[e]!=fa)
		{
			dfs1(to[e],u);
			h[to[e]]=f[to[e]]+(1-f[to[e]])*(1-ww[e]);
			f[u]*=h[to[e]];
		}
}
void dfs2(int u,int fa)
{
	double sum=val[u];int z=0;
	for (int e=head[u];e;e=nxt[e])
		if (to[e]!=fa)
			if (h[to[e]]>1e-9) sum*=h[to[e]];
			else ++z;
	for (int e=head[u];e;e=nxt[e])
		if (to[e]!=fa)
		{
			double t;
			if (h[to[e]]>1e-9)
				t=(z?0:sum/h[to[e]]*g[u]);
			else t=(z>1?0:sum*g[u]);
			g[to[e]]=t+(1-t)*(1-ww[e]);
			dfs2(to[e],u);
		}
}
int main()
{
	n=gi();
	for (int i=1;i<n;++i)
	{
		int u=gi(),v=gi(),w=gi();
		link(u,v,w);link(v,u,w);
	}
	for (int i=1;i<=n;++i)
	{
		int x=gi();
		val[i]=1.0-(double)x/100.0;
	}
	dfs1(1,0);g[1]=1;dfs2(1,0);
	for (int i=1;i<=n;++i) ans+=1.0-f[i]*g[i];
	printf("%.6lf\n",ans);
	return 0;
}
posted @ 2018-03-27 15:42  租酥雨  阅读(247)  评论(0编辑  收藏  举报