题目描述
著名的电子产品品牌 SHOI 刚刚发布了引领世界潮流的下一代电子产品——概率充电器:
“采用全新纳米级加工技术,实现元件与导线能否通电完全由真随机数决定!SHOI 概率充电器,您生活不可或缺的必需品!能充上电吗?现在就试试看吧!”
SHOI 概率充电器由 n-1 条导线连通了 n 个充电元件。进行充电时,每条导线是否可以导电以概率决定,每一个充电元件自身是否直接进行充电也由概率决定。
随后电能可以从直接充电的元件经过通电的导线使得其他充电元件进行间接充电。
作为 SHOI 公司的忠实客户,你无法抑制自己购买 SHOI 产品的冲动。在排了一个星期的长队之后终于入手了最新型号的 SHOI 概率充电器。
你迫不及待地将 SHOI 概率充电器插入电源——这时你突然想知道,进入充电状态的元件个数的期望是多少呢?
输入格式
第一行一个整数:n。概率充电器的充电元件个数。充电元件由 1-n 编号。
之后的 n-1 行每行三个整数 a, b, p,描述了一根导线连接了编号为 a 和 b 的充电元件,通电概率为 p。
第 n+2 行 n 个整数:qiq_iqi。表示 i 号元件直接充电的概率为 qi。
输出格式
输出一行一个实数,为进入充电状态的元件个数的期望,四舍五入到六位小数
样例
样例输入
3
1 2 50
1 3 50
50 0 0
样例输出
1.000000
数据范围与提示
对于 100%的数据,n≤500000,0≤p,qi≤100。
真的是个神题,学到了。读完题后很明显是树形概率dp,然而父亲儿子会相互影响,dp有后效性……然后我又想到了高斯消元,首先数据范围太大肯定不是正解,然后也不容易实现,就放弃了,打了个错解过了样例,拿了5分。(其实直接输出n可以拿10分的……)
【题解】
其实可以考虑把父亲对儿子和儿子对父亲分开考虑(T3也是两个数组dp的)。设f1[i]为i被自己或儿子点亮的概率。f2[i]为i被父亲点亮的概率;那么可以两边dfs分别求解两个数组。f1[叶子节点]=q[i];f1[i]=q[i]+
注:这里的加为概率加法,和容斥类似,P(a+b)=P(a)+P(b)-P(a)*P(b);对于i,若直接加,则多加了i即被自己点亮又被儿子点亮的情况。
f2[根节点]=f1[i];设pfa为父亲节点下传的概率,由于f2是有上向下更新,所以f2[fa]已知,pfa=f2[fa]-f1[v]*p()//解释:若fa已经点亮,则有两种情况1.fa被v点亮,2.fa不被v点亮。
所以用fa亮的概率减去fa被v点亮的概率就是fa下传的概率(这里是概率相减,P(A)=(P(A+B)-P(B))/(1-P(B)))。f2[v]=f1[v]+pfa*p();这里也是概率加法,排除v既是自己亮又被fa点亮的情
况。
Ps.还有一种方法记录的是不被点亮的概率,避免了概率的加减(其实我觉得这个也挺好理解的)。
#include<iostream>
#include<cstdio>
#include<cmath>
#define esp (1e-13)
using namespace std;
struct edge
{
int u,v,next;double p;
#define u(x) ed[x].u
#define v(x) ed[x].v
#define n(x) ed[x].next
#define p(x) ed[x].p
}ed[1000010];
int first[500010],num_e;
#define f(x) first[x]
int n;
double q[500010];
double f1[500010],f2[500010];
void dfs1(int x,int ff)
{
if(!f(x)){f1[x]=q[x];return;}
f1[x]=q[x];
for(int i=f(x);i;i=n(i))
if(v(i)!=ff)
{
dfs1(v(i),x);
f1[x]=f1[x]+f1[v(i)]*p(i)-f1[x]*f1[v(i)]*p(i);//注
}
}
void dfs2(int x,int ff)
{
double pfa=0;
for(int i=f(x);i;i=n(i))
if(v(i)!=ff)
{
if(fabs((1.0-f1[v(i)]*p(i))<esp))f2[v(i)]=1.0;
else
{
pfa=(f2[x] - f1[v(i)]*p(i))/(1.0-f1[v(i)]*p(i));//注
f2[v(i)]=f1[v(i)]+p(i)*pfa-f1[v(i)]*pfa*p(i);//注
}
dfs2(v(i),x);
}
}
inline void add_e(int u,int v,double p);
signed main()
{
// freopen("in.txt","r",stdin);
cin>>n;
int ta,tb;double tp;
for(int i=1;i<n;i++)
{
scanf("%d%d",&ta,&tb);cin>>tp;tp/=100.0;
add_e(ta,tb,tp);
add_e(tb,ta,tp);
}
for(int i=1;i<=n;i++)
cin>>q[i],q[i]/=100;
dfs1(1,0);
f2[1]=f1[1];
dfs2(1,0);
double ans=0;
for(int i=1;i<=n;i++)
ans+=f2[i];
printf("%0.6lf",ans);
}
inline void add_e(int u,int v,double p)
{
++num_e;
u(num_e)=u;
v(num_e)=v;
p(num_e)=p;
n(num_e)=f(u);
f(u)=num_e;
}