真的是一道很有意思的题目
实际上,这道题与换根没啥关系,主要是与二次扫描有点关系
1. 前置知识简单讲解
1.1. 关于问题本身
输出一行一个实数,为进入充电状态的元件个数的期望,四舍五入到六位小数。
什么是期望?(以下只是这个蒟蒻自学后所总结的,显然不会很严谨)
现在有 n n n 个事件,每个事件发生的概率为 p p p,期望值为 s s s,那么最终的期望值为:
∑ i = 1 n p i s i \sum\limits_{i=1}^np_is_i i=1∑npisi
看不懂?举个例子:
你要投一个骰子,求:的所得到的点数的期望
显然,有 6 6 6 个事件,它们发生的概率都是 1 6 \dfrac{1}{6} 61,而每一个事件的期望值分别为 1 , 2 , 3 , 4 , 5 , 6 1,2,3,4,5,6 1,2,3,4,5,6(骰子的 6 6 6 个点数),则最终的期望为 1 6 × ( 1 + 2 + 3 + 4 + 5 + 6 ) = 3 \dfrac{1}{6}\times(1+2+3+4+5+6)=3 61×(1+2+3+4+5+6)=3
而对于本问题,事件就是元件是否进入充电状态,期望值为 1 1 1(充起了就会使充起的数量 + 1 +1 +1),那么,我们只需要将所有元件进入充电状态的可能性相加即可。
1.2. 有关概率
先掏出结论:
对于两个相互独立的事件 A , B A,B A,B,它们的概率分别为 a , b a,b a,b,则:事件 A , B A,B A,B 至少发生一个的概率为 a + b − a b a+b-ab a+b−ab
证明显然:
- A A A 发生, B B B 不发生的概率: a ( 1 − b ) a(1-b) a(1−b)
- B B B 发生, A A A 不发生的概率: b ( 1 − a ) b(1-a) b(1−a)
- A , B A,B A,B 都发生: a b ab ab
把它们加起来: a ( 1 − b ) + b ( 1 − a ) + a b = a + b − a b a(1-b)+b(1-a)+ab=a+b-ab a(1−b)+b(1−a)+ab=a+b−ab
这是一个极为重要的结论,后面会多次使用
2. 题解时间
对于一个元件 i i i,想要使它充上电,无非三种情况
- 自己给自己来电(
劲啊) - 儿子给自己来电(
How 孝顺 the son is!) - 祖先或祖先的其它儿子来电
考虑到祖先,自己,儿子之间会互相影响,有点麻烦,所以,我们首先只考虑情况 1 1 1 和情况 2 2 2
我们设dp[x]
用来表示第
x
x
x 个元件充上电的概率,显然,情况
1
1
1 的概率为 a[x]/100
,情况
2
2
2 的概率为 dp[y]*z
(y
代表x
的一个儿子,z
表示连接这两个元件的导线的充电概率)
也很显然,要么只有情况
1
1
1 影响 dp[x]
,要么只有情况
2
2
2 影响 dp[x]
,要么情况
1
1
1 和情况
2
2
2 都影响 dp[x]
,两个事件至少发生一个,按照上面的公式,我们可以得到在只考虑情况
1
1
1 和情况
2
2
2 的情况下的dp[i]
代码:
void dfs(int x,int fa){
for(int i=head[x];i;i=Next[i]){
int y=ver[i];
double z=edge[i];
if(y^fa){
dfs(y,x);
dp[x]=dp[x]+dp[y]*z-dp[x]*dp[y]*z;//套公式
//这里,我将 a[x] 的值直接输入到 dp[x] 里面去了
}
}
}
接下来,考虑加入情况 3 3 3
我们只考虑父亲转移儿子
对于 x x x 元件的父亲 f f f,它的转移情况有两种
A. 不是由
x
x
x 元件转移而来(设其概率为
a
a
a)
B. 是由
x
x
x 元件转移而来(设其概率为
b
b
b)
(这里为了区分前面的
1
,
2
,
3
1,2,3
1,2,3,使用了 A,B
)
显然,如果要让
f
f
f 来影响
x
x
x,那么,
x
x
x 就不能影响
f
f
f(你丫的搁这儿搁这儿呢)
若连接
f
,
x
f,x
f,x 元件的导线的充电概率为
z
z
z,那么,情况
3
3
3 的概率实际上应为 a*z
思考: a a a 的求法
首先,不难看出,b
应该等于dp[x]*z
,那么,求a
就不难了
由于,要么只有情况A
影响dp[f]
,要么只有情况B
影响dp[f]
,要么情况A
和情况B
都影响了dp[f]
,所以,套公式:a+dp[x]*z-a*dp[x]*z=dp[f]
把a
求出来之后,就可以将情况
3
3
3 代入了
但是注意,最终求出来的a
是一个分数的形式,而分母是有可能为
0
0
0 的,注意特判
代码:
void DP(int x,int fa){
for(int i=head[x];i;i=Next[i]){
int y=ver[i];
double z=edge[i];
if(y^fa){
if(dp[y]*z<=1+0.00000001&&dp[y]*z>=1-0.00000001){//判0,注意精度
DP(y,x);
continue;
}
double tot=(dp[x]-dp[y]*z)/(1-dp[y]*z);//套公式
dp[y]=dp[y]+tot*z-dp[y]*tot*z;
DP(y,x);
}
}
}
3. 代码时间
404 Not Found
实际上,我甚至连两个关键函数都给你了,还有什么必要看完整代码吗?