Description&Data Constraint
著名的电子产品品牌SHOI刚刚发布了引领世界潮流的下一代电子产品——概率充电器:
“采用全新纳米级加工技术,实现元件与导线能否通电完全由真随机数决定!SHOI概率充电器,您生活不可或缺的必需品!能充上电吗?现在就试试看吧!”
SHOI概率充电器由 n − 1 n-1 n−1 条导线连通了 n n n 个充电元件。进行充电时,每条导线是否可以导电以概率决定,每一个充电元件自身是否直接进行充电也由概率决定。随后电能可以从直接充电的元件经过通电的导线使得其他充电元件进行间接充电。
作为SHOI公司的忠实客户,你无法抑制自己购买SHOI产品的冲动。在排了一个星期的长队之后终于入手了最新型号的SHOI概率充电器。你迫不及待地将SHOI概率充电器插入电源——这时你突然想知道,进入充电状态的元件个数的期望是多少呢?
n ≤ 500000 , 0 ≤ p , q i ≤ 100 n≤500000,0≤p,qi≤100 n≤500000,0≤p,qi≤100
题目大意
给出一个 n n n 个节点的数,每个节点有 p i p_i pi 的概率直接充电,每条边有 e u , v e_{u,v} eu,v 的概率导电,一个没有直接充电的点,想要进入充电状态需要通过几条能导电的线连接到一个直接充电的点。求出进入充电状态的点的期望值。
Solution
先把题目转换一下,每个点的贡献要么是 1,要么是 0。那么期望就转换成了概率。若我们求出每个点进入充电状态的概率 c i c_i ci,那么答案就为 ∑ c i \sum c_i ∑ci。
但是以进入充电状态为 dp 状态,会导致转移方程十分复杂,于是我们设 q i q_i qi 表示一个点不进入充电状态的概率,显然有 c i = 1 − q i c_i=1-q_i ci=1−qi。
先放式子: q i = ( 1 − p i ) ( ∏ 1 − e i , j + e i , j × p j ) ( i , j q_i=(1-p_i)(\prod 1-e_{i,j}+e_{i,j}\times p_j)(i,j qi=(1−pi)(∏1−ei,j+ei,j×pj)(i,j 联通 ) ) )。
首先点 i i i 不进入充电状态,那么就不能直接充电,概率 1 − p i 1-p_i 1−pi。
并且与 i i i 相连的点 j j j 也无法令 i i i 进入充电状态,有两种情况:
- j j j 没有进入充电状态。
- j j j 进入了充电状态但是 边 i , j i,j i,j 不导电。
情况 1 对应着 1 − e i , j 1-e_{i,j} 1−ei,j,情况 2 对应着 e i , j × p j e_{i,j}\times p_j ei,j×pj。
放在树上 q i q_i qi 表示的就是 i i i 的子树不能使 i i i 进入充电状态的概率。
但是!除了根,其他点的答案都不是完整的,缺失父亲那边的贡献。可是如果以每个点为根来递归,时间复杂度为 O ( n 2 ) O(n^2) O(n2),显然无法满足本题。
因此考虑换根。
设 f i f_i fi 表示 i i i 不进入充电状态的概率。
思考一下根从 i i i 换到 j j j 对 j j j 的答案的影响。
j j j 的答案原本缺失的是父亲 i i i 的贡献,那么父亲的总概率 f i f_i fi 除去 j j j 提供的贡献 q j q_j qj 就是父亲那边的贡献 l a s t last last。
因此式子就是:
l
a
s
t
=
f
i
/
(
1
−
e
i
,
j
+
e
i
,
j
×
q
j
)
f
j
=
q
j
×
(
1
−
e
i
,
j
+
e
i
,
j
×
l
a
s
t
)
last=f_i/(1-e_{i,j}+e_{i,j}\times q_j) \\ f_j=q_j\times(1-e_{i,j}+e_{i,j}\times last)
last=fi/(1−ei,j+ei,j×qj)fj=qj×(1−ei,j+ei,j×last)
最后答案
a
n
s
=
∑
1
−
f
i
ans=\sum1-f_i
ans=∑1−fi。
Code
#include<cstdio>
#define N 500005
using namespace std;
struct node
{
int to,next,head;
double val;
}a[N<<1];
int n,x,y,z,tot;
double ans,f[N],g[N],p[N];
void add(int x,int y,int z)
{
a[++tot].to=y;
a[tot].val=z/100.0;
a[tot].next=a[x].head;
a[x].head=tot;
}
void dfs1(int u,int fa)
{
f[u]=1-p[u];
for (int i=a[u].head;i;i=a[i].next)
{
int v=a[i].to;
if (v==fa) continue;
dfs1(v,u);
f[u]*=(1-a[i].val+a[i].val*f[v]);
}
}
void dfs2(int u,int fa,int edge)
{
if (u==1) g[u]=f[u];
else
{
double last=g[fa]/(1-a[edge].val+a[edge].val*f[u]);
g[u]=f[u]*(1-a[edge].val+a[edge].val*last);
}
for (int i=a[u].head;i;i=a[i].next)
{
int v=a[i].to;
if (v==fa) continue;
dfs2(v,u,i);
}
}
int main()
{
scanf("%d",&n);
for (int i=1;i<n;++i)
{
scanf("%d%d%d",&x,&y,&z);
add(x,y,z);add(y,x,z);
}
for (int i=1;i<=n;++i)
scanf("%d",&x),p[i]=x/100.0;
dfs1(1,0);dfs2(1,0,0);
for (int i=1;i<=n;++i)
ans+=1-g[i];
printf("%.6lf",ans);
return 0;
}