BZOJ2830 & 洛谷3830:[SHOI2012]随机树——题解

https://www.luogu.org/problemnew/show/P3830#sub   <-题面看这里~

https://www.lydsy.com/JudgeOnline/problem.php?id=2830

感觉智商被压制了的一题……后面放吐槽。

参考:https://www.cnblogs.com/GuessYCB/p/8462490.html

——————————————

对于叶结点平均深度,我们令f(x)=(a1+...+ax)/x来表示(a可以每个叶子结点(人为标号)深度的期望)。

那么我们只需要枚举每个a,然后在a上面展开?再除x?

我们为什么不用f(x-1)表示我们要展开的叶子的深度呢?于是第一问我们做完了。

——————————————

第二问设f[x][d]表示叶子数为x深度大于等于d的树的期望。

最后答案就是f[n]累加的结果(简单思考就知道为什么了)

那么对于根,枚举左右儿子挂了多少叶子即可。

一次转移就是f[x][d]=sigma f[i][d-1]+f[x-i][d-1]-f[i][d-1]*f[x-i][d-1] 最后除以x-1即可。

第一个概率是左子树深度为d-1,第二个是右子树深度为d-1,第三个是容斥。

#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
typedef double dl;
const int N=105;
int q,n;
dl f[N][N];
void solve1(){
    dl ans=0;
    for(int i=2;i<=n;i++)ans+=2.0/i;
    printf("%.6lf
",ans);
}
void solve2(){
    dl ans=0;
    for(int i=1;i<=n;i++)f[i][0]=1;
    for(int i=2;i<=n;i++){
        for(int j=1;j<=i;j++){
            for(int k=1;k<i;k++){
                f[i][j]+=f[k][j-1]+f[i-k][j-1]-f[k][j-1]*f[i-k][j-1];
            }
            f[i][j]/=i-1;
        }
    }
    for(int i=1;i<=n;i++){
        ans+=f[n][i];
    }
    printf("%.6lf
",ans);
}
int main(){
    scanf("%d%d",&q,&n);
    if(q==1)solve1();
    else solve2();
    return 0;
}

吐槽时间:

第一问我是真的傻没想到用f(x-1)来表示我们展开的结点(我还考虑a要怎么求呢……后来发现我根本不会。)

第二问考虑过设f[i]为i个叶子结点时的期望深度,设g[i]为i个叶子结点的最深的叶子的期望个数,答案就是f[i]=f[i-1]+g[i]/i。

但是推g很恶心,反正推了半天也没过样例就很gg。

+++++++++++++++++++++++++++++++++++++++++++

+本文作者:luyouqi233。               +

+欢迎访问我的博客:http://www.cnblogs.com/luyouqi233/ +

+++++++++++++++++++++++++++++++++++++++++++

原文地址:https://www.cnblogs.com/luyouqi233/p/8974714.html