Luogu P3830 [SHOI2012]随机树 | 期望 DP

题目链接

第$1$问

设$g(i)$为当有$i$个叶节点时,树的叶节点的平均深度的期望值。

假如现在有$(i-1)$个叶节点,现在要等概率地展开一个叶节点,那么展开的那个叶节点的期望深度为$g(i-1)$,展开后那两个新的叶节点的期望深度均为$(g(i-1)+1)$,叶节点深度总和的期望值增加了$(g(i-1)+2)$。

所以,$g(i)=\dfrac{(i-1)g(i-1)+g(i-1)+2}{i}=g(i-1)+\dfrac{2}{i}$

边界:$g(1)=0$

第$2$问

 设$f(i,j)$为当有$i$个叶节点时,树的深度大于等于$j$的概率。

假设现在有$i$个叶节点,树的深度大于等于$j$。

考虑枚举根节点左子树中叶节点的个数,假设有$k$个,则根节点右子树中叶节点的个数为$(i-k)$个。

现在要计算生成这样一棵树的概率是多少。

我们不妨先计算生成这样一棵树有多少种方案。

对于第一次展开,一定是展开根节点。

第一次展开后,左子树中还要展开$(k-1)$次,右子树中还要展开$(i-k-1)$次。

显然,从一个节点开始,展开$x$次的方案数有$x!$种。

那么,左子树展开的方案数即有$(k-1)!$种,右子树展开的方案数即有$(i-k-1)!$种。

对于每一次展开,我们既可以展开左子树中的叶节点,也可以展开右子树中的叶节点。

综上,生成一棵这样的树的方案数为

$(k-1)!(i-k-1)!C_{k-1+i-k-1}^{k-1}=(k-1)!(i-k-1)!\dfrac{(i-2)!}{(k-1)!(i-k-1)!}=(i-2)!$

所以,生成一棵根节点的左子树中有$k$个叶节点,右子树中有$(i-k)$个叶节点的树的方案数为$(i-2)!$,与$k$无关。

因为$1\leqslant k \leqslant i-1$且$k$为整数,且对于每一个$k$,方案数均为$(i-2)!$。

所以,生成一棵根节点的左子树中有$k$个叶节点,右子树中有$(i-k)$个叶节点的树的概率为$\dfrac{1}{i-1}$。

接着我们就来考虑,对于一棵根节点的左子树中有$k$个叶节点,右子树中有$(i-k)$个叶节点的树,树的深度大于等于$j$的概率是多少。

显然为$f(k,j-1)+f(i-k,j-1)-f(k,j-1)\cdot f(i-k,j-1)$

所以$f(i,j)=\sum\limits_{k=1}^{i-1}(f(k,j-1)+f(i-k,j-1)-f(k,j-1)\cdot f(i-k,j-1))\cdot\dfrac{1}{i-1}$ 

边界:$f(i,0)=1(1\leqslant i \leqslant n)$

$ans=\sum\limits_{i=1}^{n-1}i(f(n,i)-f(n,i+1))$

#include<iostream>
#include<cstdio>
    using namespace std;
    double g[105],f[105][105];
int main()
{
    int q=0,n=0;
    scanf("%d%d",&q,&n);
    if(q==1)
    {
        g[1]=0;
        for(int i=2;i<=n;i++)
            g[i]=g[i-1]+(double)2/i;
        printf("%.6f",g[n]);
    }
    if(q==2)
    {
        for(int i=1;i<=n;i++) f[i][0]=1;
        for(int i=2;i<=n;i++)
            for(int j=1;j<=i-1;j++)
                for(int k=1;k<=i-1;k++)
                    f[i][j]+=(f[k][j-1]+f[i-k][j-1]-f[k][j-1]*f[i-k][j-1])/(i-1);
        double ans=0;
        for(int i=1;i<=n-1;i++)
            ans+=i*(f[n][i]-f[n][i+1]);
        printf("%.6f",ans);
    }
    return 0;
}
Luogu P3830

 

posted @ 2019-07-24 16:17  wozaixuexi  阅读(183)  评论(0编辑  收藏  举报