P3830 [SHOI2012]随机树

P3830 [SHOI2012]随机树

链接

分析: 

  第一问:f[i]表示有i个叶子结点的时候的平均深度,$f[i] = \frac{f[i - 1] + 2 + f[i - 1] * (i - 1)}{2} $,表示新增加一个叶子结点,深度增加2,加权后取平均值。

  第二问:f[i][j]表示有i个叶子结点,树的深度大于等于j的概率,有$f[i][max(k, l)+ 1] = \frac{f[j][k] \times f[i - j][l]}{i - 1}$,$ans=\sum\limits_{i = 1}^{n} i * f[n][i]$。

  其中除以$i-1$表示i个叶子结点中,左儿子为j个时候的概率。因为左儿子结点只有$i-1$个取值,于是每个的概率都是$\frac{1}{i-1}$。

  枚举完左儿子的叶子结点,右儿子叶子结点也就确定了,然后左右儿子结点都是一个相同的子问题。

代码:

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<iostream>
#include<cctype>
#include<set>
#include<queue>
#include<map>
#include<vector>
#include<bitset>
using namespace std;
typedef long long LL;

inline int read() {
    int x=0,f=1;char ch=getchar();for(;!isdigit(ch);ch=getchar())if(ch=='-')f=-1;
    for(;isdigit(ch);ch=getchar())x=x*10+ch-'0';return x*f;
}

const int N = 1005;
void solve1(int n) {
    static double f[N];
    for (int i = 2; i <= n; ++i) f[i] = f[i - 1] + 2.0 / i;
    printf("%.6lf\n", f[n]);
}
void solve2(int n) {
    static double f[N][N];
    f[1][0] = 1.0;
    for (int i = 2; i <= n; ++i) 
        for (int j = 1; j < i; ++j) 
            for (int k = 0; k <= j; ++k)
                for (int l = 0; l <= (i - j); ++l) 
                    f[i][max(k, l) + 1] += f[j][k] * f[i - j][l] / (i - 1);
    double ans = 0;
    for (int i = 1; i <= n; ++i) ans += i * f[n][i];
    printf("%.6lf\n", ans);
}
int main() {
    int ty = read(), n = read();
    ty == 1 ? solve1(n) : solve2(n);    
    return 0;
}

 

posted @ 2019-04-01 11:22  MJT12044  阅读(174)  评论(0编辑  收藏  举报