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; }