测试地址:随机树
做法:本题需要用到DP+概率期望。
对于第一问,考虑令
f(n)
f
(
n
)
为有
n
n
个叶子节点的树中,叶子平均深度的期望值,我们考虑找到递推的方法。直接推这个不太好推,我们知道叶子平均深度乘上就是叶子深度和,因为期望的线性性,所以
f(n)⋅n
f
(
n
)
⋅
n
就是叶子深度和的期望值。这个就比较好推了。对于一棵树,原本的叶子深度和为
s
s
,如果展开一个深度为的叶子,那么就会产生两个深度为
x+1
x
+
1
的叶子,那么新的叶子深度和就是
s+x+2
s
+
x
+
2
,而选到一个叶子的概率是
1n
1
n
,所以平均下来,叶子深度和等于原叶子深度和,加上叶子的平均深度,再加上
2
2
。又根据期望的线性性,新的叶子深度和的期望——,等于原叶子深度和的期望
f(n−1)⋅(n−1)
f
(
n
−
1
)
⋅
(
n
−
1
)
,加上叶子平均深度的期望
f(n−1)
f
(
n
−
1
)
,再加上
2
2
的期望——本身,得到下面的式子。
f(n)⋅n=f(n−1)⋅(n−1)+f(n−1)+2
f
(
n
)
⋅
n
=
f
(
n
−
1
)
⋅
(
n
−
1
)
+
f
(
n
−
1
)
+
2
所以有:
f(n)=f(n−1)+2n
f
(
n
)
=
f
(
n
−
1
)
+
2
n
。
于是第一个问题就解决了。第二个问题稍微麻烦一些,我们根据期望的公式有:
E[x]=∑∞i=1P(x≥i)
E
[
x
]
=
∑
i
=
1
∞
P
(
x
≥
i
)
那么我们只需算出
g(n,d)
g
(
n
,
d
)
:有
n
n
个叶子节点的树中,深度大于等于的概率。首先我们要证明一个结论:有
n
n
个叶子节点的树中,根的左子树中的叶子节点数量为~
n−1
n
−
1
中任何整数的概率相等。这个可以归纳证明,网上各位大佬都不屑于写,本蒟蒻就在这里补充一下:
令
P(n,L)
P
(
n
,
L
)
为有
n
n
个叶子节点的树中,根的左子树有个叶子节点的概率。当
n=2
n
=
2
时,结论
P(n,L)=1n−1
P
(
n
,
L
)
=
1
n
−
1
显然是成立的。而当
n>2
n
>
2
时,假设对于
k<n
k
<
n
结论都成立,首先
P(n,1)=12⋅23⋅...⋅n−2n−1=1n−1
P
(
n
,
1
)
=
1
2
⋅
2
3
⋅
.
.
.
⋅
n
−
2
n
−
1
=
1
n
−
1
(每一步展开都选择右子树中叶子节点,因此将概率相乘),而对于
L>1
L
>
1
,因为有递推式:
P(n,L)=L−1n−1P(n−1,L−1)+n−L−1n−1P(n−1,L)
P
(
n
,
L
)
=
L
−
1
n
−
1
P
(
n
−
1
,
L
−
1
)
+
n
−
L
−
1
n
−
1
P
(
n
−
1
,
L
)
也是根据选择的概率进行递推。将
P(n−1,L)=1n−2
P
(
n
−
1
,
L
)
=
1
n
−
2
代入简化后,得
P(n,L)=1n−1
P
(
n
,
L
)
=
1
n
−
1
,因此结论成立。
有了这个结论之后,就能得到下列式子:
g(n,d)=1n−1∑n−1i=1g(i,d−1)+g(n−i,d−1)−g(i,d−1)⋅g(n−i,d−1)
g
(
n
,
d
)
=
1
n
−
1
∑
i
=
1
n
−
1
g
(
i
,
d
−
1
)
+
g
(
n
−
i
,
d
−
1
)
−
g
(
i
,
d
−
1
)
⋅
g
(
n
−
i
,
d
−
1
)
减掉的那个部分的含义是,前面的
g(i,d−1)+g(n−i,d−1)
g
(
i
,
d
−
1
)
+
g
(
n
−
i
,
d
−
1
)
把两边都
≥d
≥
d
的概率多算了一遍,所以要减掉。那么我们就以
O(n3)
O
(
n
3
)
的时间复杂度解决了这一题。
以下是本人代码:
#include <bits/stdc++.h>
using namespace std;
int q,n;
double f[110]={0},g[110][110]={0};
int main()
{
scanf("%d%d",&q,&n);
if (q==1)
{
for(int i=2;i<=n;i++)
f[i]=f[i-1]+2.0/(double)i;
printf("%.6lf",f[n]);
}
else
{
g[1][0]=1.0;
for(int i=2;i<=n;i++)
{
g[i][0]=1.0;
for(int j=1;j<i;j++)
{
for(int k=1;k<i;k++)
g[i][j]+=g[k][j-1]+g[i-k][j-1]-g[k][j-1]*g[i-k][j-1];
g[i][j]/=(double)(i-1);
}
}
double ans=0.0;
for(int i=1;i<n;i++)
ans+=g[n][i];
printf("%.6lf",ans);
}
return 0;
}