[SHOI2014]概率充电器 (树形DP)

版权声明:本文为博主原创文章,未经博主允许必须转载。 https://blog.csdn.net/qq_35950004/article/details/88183226

题目
这个题。。。
如果我们只考虑从儿子到父亲的传输电,之后可以再用一个dfs从上至下更新父亲对儿子的贡献。
但是这个题需要求的是概率而非期望(直接算期望不好求),就没有那些什么线性性:
P ( A B ) = P ( A ) + P ( B ) P ( A ) P ( B ) P(A|B) = P(A) + P(B) - P(A) * P(B)
P ( A ) = P ( A B ) P ( B ) 1 P ( B ) P(A)=\frac {P(A|B)-P(B)}{1-P(B)}
注意对于第二个式子,Claris大佬认为会除0,于是还存了当前的概率上有几个0,但其实是不需要的,如果 P ( B ) = 1 P(B)=1 ,那么。。。下面是一定会通电的,直接赋值 d p = 1 dp=1 就行
A C   C o d e : {\rm AC \ Code:}

#include<bits/stdc++.h>
#define maxn 500005
#define double long double
using namespace std;

int n;
int info[maxn],Prev[maxn<<1],to[maxn<<1],cnt_e;
double cst[maxn<<1];
inline void Node(int u,int v,double ct){ Prev[++cnt_e]=info[u],info[u]=cnt_e,to[cnt_e]=v,cst[cnt_e]=ct; }

double zc[maxn];

double dp[maxn];

void dfs(int now,int ff)
{
	dp[now] = zc[now];
	for(int i=info[now];i;i=Prev[i])
		if(to[i]!=ff)
		{
			dfs(to[i],now);
			double tmp = dp[to[i]] * cst[i];
			dp[now] = dp[now] + tmp - dp[now] * tmp;
		}
}

double dp2[maxn],ans;
void ser(int now,int ff)
{
	ans +=dp2[now];
	for(int i=info[now];i;i=Prev[i])
		if(to[i]!=ff)
		{
			double tmp = dp[to[i]] * cst[i];
			if(fabs(tmp - 1)<1e-8) dp2[to[i]] = 1;
			else
			{
				double pfa = (dp2[now]-tmp)/(1-tmp)*cst[i];
				dp2[to[i]] = dp[to[i]] + pfa - pfa * dp[to[i]];
			}
			ser(to[i],now);
		}
}

int main()
{
	scanf("%d",&n);
	for(int i=1;i<n;i++)
	{
		int u,v,p;
		scanf("%d%d%d",&u,&v,&p);
		
		Node(u,v,p/100.0),Node(v,u,p/100.0);
	}
	for(int i=1;i<=n;i++) scanf("%Lf",&zc[i]),zc[i]/=100;
	
	dfs(1,0);
	dp2[1] = dp[1];
	ser(1,0);
	
	printf("%.6Lf",ans);
}

猜你喜欢

转载自blog.csdn.net/qq_35950004/article/details/88183226
今日推荐