概率充电器

概率充电器

题解

很简单的一道树形dp,不过我们得用容斥的方法转变一下思路。

如果我们将dp值设为灯亮的几率,我们的时间复杂度可能被卡到O\left ( 2^{n}n \right ),显然是不行的,我们也就不具体分析了。

我们设dp1_{i}为ti不被它的子树上任意一点亮的概率(含自身)。

很显然,dp1(以下省略1)的转移方程式如下:

dp_{u}=(1-q_{u})\prod _{v\in u's\, sons}((1-w)+dp_{v}-(1-w)dp_{v})

u点不能被点亮的情况为它自身不亮且它的儿子不能将它点亮的概率的乘积。

然后,我们可以用换根的想法改一改,设当前的u为根。dp2_{i}即为i点不被点亮的概率。

很显然,dp2(一下令dp1为f,省略2)的转移方程式如下:

v为u的子节点,接下来我们将v换为根。

P为v为根时u子树点不亮v的概率:P=\frac{dp_{u}}{(1-w)+f_{v}-(1-w)f_{v}}

dp_{v}=f_{v}((1-w)+P-(1-w)P)

那么,答案即为\sum_{i=1}^{n}(1-dp_{i})

源码

#include<cstdio>
#include<cmath>
#include<iostream>
#include<algorithm>
#include<queue>
#include<map>
#include<vector>
#define re register
#define MAXN 500005
using namespace std;
typedef long long LL;
#define int LL
const int INF=0x7f7f7f7f;
int from[MAXN<<1],to[MAXN<<1],nxt[MAXN<<1];
int head[MAXN],tot,n;
double paid[MAXN<<1],q[MAXN],ans;
double dp1[MAXN],dp2[MAXN];
#define gc() getchar()
template<typename _T>
inline void read(_T &x){
	_T f=1;x=0;char s=gc();
	while(s>'9'||s<'0'){if(s=='-')f=-1;s=gc();}
	while(s>='0'&&s<='9'){x=(x<<3)+(x<<1)+(s^48);s=gc();}
	x*=f;
}
void addEdge(int u,int v,double w){
	from[++tot]=u;to[tot]=v;paid[tot]=w;
	nxt[tot]=head[u];head[u]=tot;
}
void dfs1(int u,int fa){
	dp1[u]=1.0-q[u];
	for(int i=head[u];i;i=nxt[i]){
		int v=to[i];double w=1.0-paid[i];
		if(v==fa) continue;
		dfs1(v,u);dp1[u]*=(w+dp1[v]-w*dp1[v]);
	}
}
void dfs2(int u,int fa){
	for(int i=head[u];i;i=nxt[i]){
		int v=to[i];double w=1.0-paid[i];
		if(v==fa) continue;
		double P=dp2[u]/(w+dp1[v]-w*dp1[v]);
		dp2[v]=dp1[v]*(w+P-w*P);
		dfs2(v,u);
	}
}
signed main()
{	
	read(n);
	for(int i=1;i<n;i++){
		int u,v,w;read(u);read(v);read(w);
		addEdge(u,v,0.01*w);addEdge(v,u,0.01*w);
	}
	for(int i=1;i<=n;i++) scanf("%lf",&q[i]),q[i]*=0.01;
	dfs1(1,0);dp2[1]=dp1[1];dfs2(1,0);
	for(int i=1;i<=n;i++) ans+=1-dp2[i];
	printf("%.6lf",ans);
	return 0;
}

谢谢!!!

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值