【洛谷 P4284】【树形DP】【期望】 概率充电器

58 篇文章 0 订阅
51 篇文章 0 订阅

【洛谷 P4284】【树形DP】【期望】 概率充电器

题目

在这里插入图片描述
在这里插入图片描述


解题思路

考试的时候就很懵
样例都没推出来
???我大概是脑子烧坏了

直接求出开的概率还是很难想的
可以想到求出关的概率
然后用1-关的概率就是开的概率

在这里插入图片描述
假设将图建出来长这个样子
设 f[i] 是第i个节点不充电的概率 (其子树以及它自己转移过来的概率)
考虑 j 这个节点能对 i 做出什么贡献
既然要让 i 是关的 那么ta自己得是关的 边不能传电 或是 j 没有电边能传电
那么式子就是
f[i] = (1-p[i])(1 - h[i][j] + h[i][j] * f[j]) (j是i的子节点)

这样求出来的只有根是确定的概率
别的点并不确定
假设我们要 j 为根
那么就会分为三个部分 原有的子树 根除ta的部分 根与它的连接
在这里插入图片描述
设 g[i] 为第i个点关的概率(所有情况)
如果 i 是第一次求时的根 g[i]=f[i]
否则 先求出根除ta的部分(除去ta的贡献即可)
res = g[fa] / (1-h[fa][i] + h[fa][i] * f[i])
然后将这贡献放入g[i]中
g[i] = f[i] * (1-h[fa][i] + h[fa][i] * res)

最后答案就是累加所有的1-g[i]
关于在gmoj只有80这件事,不知为啥RE了


代码

#include<iostream>
#include<cstdio>
using namespace std;
struct lzf{
	int to,next;
	double z;
}h[1000010];
int n,x,y,t,z,head[500010];
double ans,p[500010],f[500010],g[500010];
void add(int x,int y,int z)
{
	 h[++t].to=y;
	 h[t].z=z*0.01;
	 h[t].next=head[x];
	 head[x]=t;
	 h[++t].to=x;
	 h[t].z=z*0.01;
	 h[t].next=head[y];
	 head[y]=t;
}
void dfs(int id,int fa)
{
	f[id]=1-p[id];  //自己不开的概率
	for (int i=head[id];i;i=h[i].next)
		if (h[i].to!=fa)
		{
		   dfs(h[i].to,id);
		   f[id]*=(1-h[i].z+h[i].z*f[h[i].to]);  //乘上子节点不开的概率
		}
}
void zdfs(int id,int fa,int b)  //b是指用的哪条边,因为要知道这条边的概率
{
	 if (id==1)
	    g[id]=f[id];  //是根,直接取值
	    else{
	    	 double res=g[fa]/(1-h[b].z+h[b].z*f[id]);
	    	  g[id]=f[id]*(1-h[b].z+h[b].z*res);  //更新答案
		}
	 for (int i=head[id];i;i=h[i].next)  
	     if (h[i].to!=fa)
	         zdfs(h[i].to,id,i);
}
int main()
{
	scanf("%d",&n);
	for (int i=1;i<n;i++)
	{
		scanf("%d%d%d",&x,&y,&z);
		add(x,y,z);
	}
	for (int i=1;i<=n;i++)
	{
	    scanf("%lf",&p[i]);
	    p[i]*=0.01;
	}
	dfs(1,0);  //先求出根的概率
	zdfs(1,0,0);  //求所有点的概率
	for (int i=1;i<=n;i++)
	    ans+=1-g[i];
	printf("%.6lf",ans);
	return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值