测试地址:随机树
做法:本题需要用到DP+概率期望。
对于第一问,考虑令为有个叶子节点的树中,叶子平均深度的期望值,我们考虑找到递推的方法。直接推这个不太好推,我们知道叶子平均深度乘上就是叶子深度和,因为期望的线性性,所以就是叶子深度和的期望值。这个就比较好推了。对于一棵树,原本的叶子深度和为,如果展开一个深度为的叶子,那么就会产生两个深度为的叶子,那么新的叶子深度和就是,而选到一个叶子的概率是,所以平均下来,叶子深度和等于原叶子深度和,加上叶子的平均深度,再加上。又根据期望的线性性,新的叶子深度和的期望——,等于原叶子深度和的期望,加上叶子平均深度的期望,再加上的期望——本身,得到下面的式子。
所以有:。
于是第一个问题就解决了。第二个问题稍微麻烦一些,我们根据期望的公式有:
那么我们只需算出:有个叶子节点的树中,深度大于等于的概率。首先我们要证明一个结论:有个叶子节点的树中,根的左子树中的叶子节点数量为~中任何整数的概率相等。这个可以归纳证明,网上各位大佬都不屑于写,本蒟蒻就在这里补充一下:
令为有个叶子节点的树中,根的左子树有个叶子节点的概率。当时,结论显然是成立的。而当时,假设对于结论都成立,首先(每一步展开都选择右子树中叶子节点,因此将概率相乘),而对于,因为有递推式:
也是根据选择的概率进行递推。将代入简化后,得,因此结论成立。
有了这个结论之后,就能得到下列式子:
减掉的那个部分的含义是,前面的把两边都的概率多算了一遍,所以要减掉。那么我们就以的时间复杂度解决了这一题。
以下是本人代码:
#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;
}