P4284 [SHOI2014]概率充电器

题目描述

著名的电子产品品牌SHOI 刚刚发布了引领世界潮流的下一代电子产品—— 概率充电器:

“采用全新纳米级加工技术,实现元件与导线能否通电完全由真随机数决 定!SHOI 概率充电器,您生活不可或缺的必需品!能充上电吗?现在就试试看 吧!”

SHOI 概率充电器由 \(n-1\) 条导线连通了 \(n\) 个充电元件。进行充电时,每条导线是否可以导电以概率决定,每一个充电元件自身是否直接进行充电也由概率决定。随后电能可以从直接充电的元件经过通电的导线使得其他充电元件进行 间接充电。

作为 SHOI 公司的忠实客户,你无法抑制自己购买 SHOI 产品的冲动。在排了一个星期的长队之后终于入手了最新型号的 SHOI 概率充电器。你迫不及待地将 SHOI 概率充电器插入电源——这时你突然想知道,进入充电状态的元件个数的期望是多少呢?

输入输出格式

输入格式:

第一行一个整数:\(n\)。概率充电器的充电元件个数。充电元件由 \(1-n\) 编号。

之后的 \(n-1\) 行每行三个整数\(a,b,p\) ,描述了一根导线连接了编号为 \(a\) 和 \(b\) 的 充电元件,通电概率为\(p\%\)。

第 \(n+2\) 行 \(n\) 个整数:\(q_i\)。表示 \(i\) 号元件直接充电的概率为 \(q_i\%\)。

输出格式:

输出一行一个实数,为能进入充电状态的元件个数的期望,四舍五入到小数点后6位小数。

说明

对于\(30\%\)的数据,\(n≤5000\)。

对于\(100\%\)的数据,\(n≤500000\),\(0≤p,q_i≤100\)。


果然一做期望题就暴露自己脑子蠢的事实了=-=

首先我读错题了,但是我觉得不能怪我啊,难道题目说的不就是只有自己激发的才能传火给别人嘛...

然后我花了好久才发现这个题就是在算概率。

每个点可以从所有其他点获取传火概率,做一个换跟dp就行了,具体的

\(dp_i\)先处理出子树\(i\)的贡献,然后直接换跟做就可以了。

需要注意的地方,传火的时候要减去两个都有,换跟中有一个类似退背包的过程需要处理分母为0


Code:

#include <cstdio>
#include <cmath>
const int N=5e5+10;
const double eps=1e-8;
int head[N],to[N<<1],Next[N<<1],cnt;
double dp[N],ans,edge[N<<1];
void add(int u,int v,double w)
{
to[++cnt]=v,edge[cnt]=w,Next[cnt]=head[u],head[u]=cnt;
}
void dfs1(int now,int fa)
{
for(int v,i=head[now];i;i=Next[i])
if((v=to[i])!=fa)
{
dfs1(v,now);
dp[now]=dp[now]+dp[v]*edge[i]-dp[now]*dp[v]*edge[i];
}
}
void dfs2(int now,int fa,double p)
{
dp[now]=dp[now]+p-dp[now]*p;
ans+=dp[now];
for(int v,i=head[now];i;i=Next[i])
if((v=to[i])!=fa)
{
if(fabs(dp[v]*edge[i]-1)<eps) p=dp[now];
else p=(dp[now]-dp[v]*edge[i])/(1-dp[v]*edge[i]);
dfs2(v,now,p*edge[i]);
}
}
int main()
{
int n;scanf("%d",&n);
for(int u,v,w,i=1;i<n;i++)
{
scanf("%d%d%d",&u,&v,&w);
add(u,v,1.0*w/100),add(v,u,1.0*w/100);
}
for(int i=1;i<=n;i++) scanf("%lf",dp+i),dp[i]/=100.0;
dfs1(1,0);
dfs2(1,0,0);
printf("%.6lf\n",ans);
return 0;
}

2019.1.13

05-11 17:01