[SH2014] 概率充电器 题解

Tag : 换根 dp

引入 :

例题1 : STA-Station
例题2 : 中心

f [ u ] : f[u] : f[u]: u u u 子树内的答案
g [ u ] : g[u]: g[u]: u u u 为根整体的答案
在这里插入图片描述

做换根 dp 要算清楚转移根的过程,算清楚减了什么,加了什么

  • 中心 为例
    在这里插入图片描述
    若当前是以 u u u 为根,先要转移为以 u u u 的儿子 v v v 为根,那么如图所示,
    会影响粉色和绿色部分,粉色部分会少走图中标注的橙色边 ( − w 1 ∗ s i z e [ v ] -w_1*size[v] w1size[v]),绿色部分会多走图中标注的蓝色边 ( + w 2 ∗ ( n − s i z e [ v ] ) +w_2*(n-size[v]) +w2(nsize[v])),所以 g [ v ] = g [ u ] − w 1 ∗ s i z e [ v ] + w 2 ∗ ( n − s i z e [ v ] ) g[v]=g[u]-w_1*size[v]+w_2*(n-size[v]) g[v]=g[u]w1size[v]+w2(nsize[v])
//中心
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int MAXN=1e5+5;
int n, f[MAXN], g[MAXN], siz[MAXN];
struct Edge{
	int to,w1,w2;
};
vector <Edge> G[MAXN]; 
void dfs1(int u,int fa)
{
	siz[u]=1;
	for(int i=0;i<G[u].size();i++)
	{
		int v=G[u][i].to, w=G[u][i].w1;
		if(v==fa) continue;
		dfs1(v,u);
		siz[u]+=siz[v];
		f[u]+= f[v] + w * siz[v];
	}
} 
void dfs2(int u,int fa)
{
	for(int i=0;i<G[u].size();i++)
	{
		int v=G[u][i].to;
		if(v==fa) continue;
		g[v] = g[u] - G[u][i].w1 * siz[v] + G[u][i].w2 * (n-siz[v]);
		dfs2(v,u);
	}
}
signed main()
{
	cin>>n;
	for(int i=1,u,v,w1,w2;i<n;i++)
	{
		cin>>u>>v>>w1>>w2; 
		G[u].push_back((Edge){v,w1,w2});
		G[v].push_back((Edge){u,w2,w1});
	}
	
	dfs1(1,0);	 g[1]=f[1];
	dfs2(1,0);
	
	int res=g[1];
	for(int i=1;i<=n;i++)
		res=min(res,g[i]);
	cout<<res;
	return 0;
}
正文

SHOI2014 概率充电器

对于一个节点,其是否能通电取决于三者: 儿子,父亲,自身

  • 自己通电
  • 儿子通电,且儿子与该节点的边能通电
  • 父亲通电,且父亲与该节点的边能通电

f [ u ] f[u] f[u] 表示在 u u u 子树内, u u u 通电的概率
g [ u ] g[u] g[u] 表示以 u u u 为根, u u u 通电的概率 (不光只靠儿子,还靠父亲)

  • 先考虑 f [ u ] f[u] f[u]

  • f [ u ] f[u] f[u] 只可能自己通电或被儿子通电

  • 我们记 自己通电概率为 A A A ,靠儿子通电概率为 B B B ,两者其中至少一个发生 u u u 即通电

  • 两者不发生的概率为 ( 1 − A ) ∗ ( 1 − B ) (1-A)*(1-B) (1A)(1B) ,那么至少一个发生的概率即为 1 − ( 1 − A ) ∗ ( 1 − B ) 1-(1-A)*(1-B) 1(1A)(1B) A + B − A ∗ B A+B-A*B A+BAB

void dfs1(int u,int fa)
{
	for(int i=0;i<G[u].size();i++)
	{
		int v=G[u][i].to; double w=G[u][i].w;
		if(v==fa) continue;
		dfs1(v,u);
		double A=f[u], B=f[v]*w;
		f[u] = A+B-A*B;
	}
}
//main
	for(int i=1;i<=n;i++)
	{
		cin>>a[i]; 
		a[i]=0.01*a[i]; f[i]=a[i];
	}
	dfs1(1,0);
  • 接着考虑 g [ u ] g[u] g[u]
  • 假设当前以 u u u 为根,现需转移为以 v v v 为根
  • 我们记 u u u 不靠 v v v v v v 子树通电的概率为 x [ u ] x[u] x[u]

在这里插入图片描述

  • 现转移:

在这里插入图片描述

  • 可得 g [ v ] = f [ v ] + w ∗ x [ u ] − f [ v ] ∗ w ∗ x [ u ] g[v]=f[v]+w*x[u]-f[v]*w*x[u] g[v]=f[v]+wx[u]f[v]wx[u] w w w 为边 u , v u,v u,v 通电的概率

  • 那么求出 x [ u ] x[u] x[u] 即可转移

  • 由于 g [ u ] = x [ u ] + f [ v ] ∗ w − x [ u ] ∗ f [ v ] ∗ w g[u]=x[u]+f[v]*w-x[u]*f[v]*w g[u]=x[u]+f[v]wx[u]f[v]w w w w 为边 u , v u,v u,v 通电的概率

  • x [ u ] = ( g [ u ] − f [ v ] ∗ w ) / ( 1 − f [ v ] ∗ w ) x[u]=(g[u]-f[v]*w)/(1-f[v]*w) x[u]=(g[u]f[v]w)/(1f[v]w)

  • 即可转移 g [ v ] g[v] g[v]

void dfs2(int u,int fa)
{
	for(int i=0;i<G[u].size();i++)
	{
		int v=G[u][i].to; double w=G[u][i].w;
		if(v==fa) continue;
		if(1 - f[v] * w <= eps) g[v]=f[v];
		else
		{
			double A = w * (g[u] - f[v] * w) / (1 - f[v] * w);
			double B = f[v];
			g[v] = A+B-A*B;
		}
		dfs2(v,u);
	}
}
#include<bits/stdc++.h>
using namespace std;
const int MAXN=5e5+5;
const double eps = 1e-9;
int n;
double a[MAXN], f[MAXN], g[MAXN]; 
struct Node{
	int to;
	double w;
};
vector <Node> G[MAXN];
void dfs1(int u,int fa)
{
	for(int i=0;i<G[u].size();i++)
	{
		int v=G[u][i].to; double w=G[u][i].w;
		if(v==fa) continue;
		dfs1(v,u);
		double A=f[u], B=f[v]*w;
		f[u] = A+B-A*B;
	}
}
void dfs2(int u,int fa)
{
	for(int i=0;i<G[u].size();i++)
	{
		int v=G[u][i].to; double w=G[u][i].w;
		if(v==fa) continue;
		if(1 - f[v] * w <= eps) g[v]=f[v];
		else
		{
			double A = w * (g[u] - f[v] * w) / (1 - f[v] * w);
			double B = f[v];
			g[v] = A+B-A*B;
		}
		dfs2(v,u);
	}
}
int main()
{
	cin>>n;
	for(int i=1;i<n;i++)
	{
		int u,v,w;
		cin>>u>>v>>w;
		G[u].push_back((Node){v,1.0*w/100});
		G[v].push_back((Node){u,1.0*w/100});
	}
	for(int i=1;i<=n;i++)
	{
		cin>>a[i]; 
		a[i]=0.01*a[i]; f[i]=a[i];
	}
	dfs1(1,0);
	g[1]=f[1];
	dfs2(1,0);
	double ans=0;
	for(int i=1;i<=n;i++)
		ans+=g[i]; 
	printf("%.6lf",ans);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值