[SHOI2014]概率充电器

一、题目

点此看题

二、解法

其实这道题主要考察对期望的理解。

0x01 错解
d p [ u ] dp[u] dp[u] u u u点被点亮的期望,很容易想到的思路是先跑子树内的,再跑子树外的,转移是难点。
一开始写的转移是 d p [ u ] = ∑ d p [ v ] × c + q [ u ] dp[u]=\sum dp[v]\times c+q[u] dp[u]=dp[v]×c+q[u],也就是暴力加起来。
子树外的转移就省略了吧,反正也是错的。
思考问题到底在哪里,其实就是交集不能求和,也就是我们只需要有一个地方给 u u u供电,但存在多个地方给 u u u供电的情况,我们就成功的 g g gg gg了。

0x02 改变状态定义
上文说明了求被点亮的期望是很难的,那我们换一种思路,定义 d p [ u ] dp[u] dp[u] u u u不被点亮的期望,为了实现方便,定义 d p [ u ] [ 0 / 1 ] dp[u][0/1] dp[u][0/1]为 单独考虑子树内 / / /子树外 不被点亮的期望,考虑转移。

  • 子树内, d p [ u ] [ 0 ] = ( ∏ 1 − c + d p [ v ] [ 0 ] ) × ( 1 − p [ i ] ) dp[u][0]=(\prod 1-c+dp[v][0])\times (1-p[i]) dp[u][0]=(1c+dp[v][0])×(1p[i]),这个 d p dp dp式的本质是分类讨论,也就是当边断开的期望加上边不断开并且 v v v没有亮,在乘上 u u u不亮的期望。
  • 子树外, d p [ v ] [ 1 ] = 1 − c + ( d p [ u ] [ 1 ] × d p [ u ] [ 0 ] / ( 1 − c + d p [ v ] [ 0 ] × c ) ) × c dp[v][1]=1-c+(dp[u][1]\times dp[u][0]/(1-c+dp[v][0]\times c))\times c dp[v][1]=1c+(dp[u][1]×dp[u][0]/(1c+dp[v][0]×c))×c,也就是先算出 f a fa fa不亮的期望乘上边不断的期望,再加上边断掉的期望,注意小区 v v v子树对于 u u u的贡献,除掉即可。

最后答案即为 ∑ i = 1 n 1 − d p [ i ] [ 0 ] × d p [ i ] [ 1 ] \sum^{n}_{i=1} 1-dp[i][0]\times dp[i][1] i=1n1dp[i][0]×dp[i][1],时间复杂度 O ( n ) O(n) O(n)

#include <cstdio>
#define eps 1e-9
const int MAXN = 500005;
int read()
{
	int x=0,flag=1;char c;
	while((c=getchar())<'0' || c>'9') if(c=='-') flag=-1;
	while(c>='0' && c<='9') x=(x<<3)+(x<<1)+(c^48),c=getchar();
	return x*flag;
}
int n,tot,f[MAXN],q[MAXN];
double ans,dp[MAXN][2];
struct edge
{
	int v,c,next;
}e[2*MAXN];
void dfs1(int u,int fa)
{
	dp[u][0]=(1-1.0*q[u]/100);
	for(int i=f[u];i;i=e[i].next)
	{
		int v=e[i].v;double c=1.0*e[i].c/100;
		if(v==fa) continue ;
		dfs1(v,u);
		dp[u][0]*=(1-c+dp[v][0]*c);
	}
}
void dfs2(int u,int fa)
{
	for(int i=f[u];i;i=e[i].next)
	{
		int v=e[i].v;double c=1.0*e[i].c/100;
		if(v==fa) continue;
		if(1-c+dp[v][0]*c>eps)//解决精度问题,太小了就把它当不存在
			dp[v][1]=1-c+(dp[u][1]*dp[u][0]/(1-c+dp[v][0]*c))*c;
		else
			dp[v][1]=1-c;
		dfs2(v,u);
	}
}
int main()
{
	n=read();
	for(int i=2;i<=n;i++)
	{
		int u=read(),v=read(),c=read();
		e[++tot]=edge{v,c,f[u]},f[u]=tot;
		e[++tot]=edge{u,c,f[v]},f[v]=tot;
	}
	for(int i=1;i<=n;i++)
		q[i]=read();
	dfs1(1,0);
	dp[1][1]=1;
	dfs2(1,0);
	for(int i=1;i<=n;i++)
		ans+=(1-dp[i][0]*dp[i][1]);
	printf("%.6lf",ans);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值