In mathematics you don't understand things, you just get used to them.

link.

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

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

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

dp trees probablities

拟阵 ⭐ 回归 ⭐ 线性基
上一篇 «
「joisc - 2021 d2t2」道路の建設案
» 下一篇