【LuoguP3830】随机树(SHOI2012)-DP+概率期望

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

#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;
}
posted @ 2018-08-31 22:15  Maxwei_wzj  阅读(147)  评论(0编辑  收藏  举报