Solution -「SHOI 2012」随机数

cirnovsky /

link.

对于 pass 1, 你把他考虑成 xi\frac{\sum x}{i} 的形式, 于是每次操作的贡献就是 2i\frac{2}{i}, 那么答案就是 i=2n2i\sum_{i=2}^n\frac{2}{i}.

对于 pass 2, 首先由全概率公式 E(n)=i=0P(ni)\textbf{E}(n)=\sum_{i=0}^{\infty}\textbf{P}(n\geqslant i). 那么我们考虑固定 k=ik=i, 求出 P(nk)\textbf P(n\geqslant k). 其实直接求这个有点麻烦, 强化一下条件, 我们求 P(n=k)\textbf P(n=k), 最后后缀和即可取得 P(nk)\textbf P(n\geqslant k).

f(i,j)f(i,j) 表示共 ii 个叶结点, 树深度为 jj 时的概率. 对于二叉树我们有一种经典的考虑子结构的方式, 即考虑左子树的信息. 对于此题, 我们考虑左子树 (相对于根结点来说) 的叶结点数量 kk. 那么有 f(i,max{k,l}+1)f(i,max{k,l}+1)+f(j,k)×f(ij,l)i1f(i,\max\{k,l\}+1)\gets f(i,\max\{k,l\}+1)+\frac{f(j,k)\times f(i-j,l)}{i-1}. 其中 ii 为全树的叶子个数, kk, ll 分别为左, 右子树的深度, jj 为左子树的叶结点个数. 1i1\frac{1}{i-1} 是对于一个有 ii 个叶子的二叉树, 有 kk 个结点在左子树的概率, which 与 kk 无关. 至于为什么是 1i1\frac{1}{i-1}, 你考虑操作次数为 i1i-1 即可.

#include <bits/stdc++.h>
using namespace std;
typedef double db;
int Q, N;
db f[200][200];
signed main() {
  ios::sync_with_stdio(0), cin.tie(0);
  cin >> Q >> N;
  cout << fixed;
  cout << setprecision(6);
  if (Q == 1) {
    db ans = 0;
    for (int i = 2; i <= N; ++i) {
      ans += 2.0 / i;
    }
    cout << ans << "\n";
  } else if (Q == 2) {
    f[1][0] = f[2][1] = f[3][2] = 1;
    for (int i = 4; 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);
          }
        }
      }
    }
    db ans = 0;
    for (int i = N; i >= 1; --i) {
      f[N][i] += f[N][i + 1];
    }
    for (int i = 1; i < N; ++i) {
      ans += f[N][i];
    }
    cout << ans << "\n";
  }
  return 0;
}