题解:[TJOI2015] 概率论

洛谷P3978 | “数学题” | 卡特兰数

Posted by TH911 on July 14, 2025

题目传送门

题意分析

首先,对于这种输入只有一个数,从这一个数就能推出答案的题目,大概率是个毒瘤数学题。如此考虑求解。

求期望,期望即总叶节点数除以不同构的二叉树个数。

令 $n$ 个节点的不同构二叉树个数为 $f_n$,其叶节点总数为 $g_n$,则答案即 $\dfrac{g_n}{f_n}$。

考虑如何求解 $f_n,g_n$。

先考虑求解 $f_n$。其实 $f_n$ 的求解是较为简单的,因为二叉树具有一个性质:根节点的左右子节点的子树仍然是二叉树

令左子树的大小为 $k$,则右子树的大小为 $n-k-1$,总数 $f_n$ 即左子树与右子树组合得到的结果:

\[f_n=\sum_{k=0}^{n-1}f_kf_{n-k-1}\]

可以发现,此即 Catalan 数

由 Catalan 数通项公式(只需要使用生成函数+牛顿二项式定理即可解得),可得:

\[f_n=\dfrac{1}{2n+1}\dbinom{2n}{n}\]

现在考虑求解 $g_n$。

二叉树具有一个性质:如果给大小为 $n-1$ 的二叉树每一个最外层的节点补满左右子节点,则添加的节点数为 $n$

考虑数学归纳法。

当 $n=2$ 时,添加的 $2$ 个节点即根节点的左右子节点,命题成立。

假设 $n=k$ 时命题成立。

当 $n=k+1$ 时,可视为在某个 $k$ 个节点的树上添加了一个节点得到;设添加节点为 $v$,其父节点为 $u$。

$u$ 原本可以添加 $2$ 个节点,现在因为有了子节点 $v$,因此对答案的贡献减少了 $1$。但是 $v$ 对答案有大小为 $2$ 的贡献,因此 $n=k+1$ 时的答案为 $n=k$ 时的答案加 $1$,即 $k+1=n$。

故,原命题成立。

那么就可以枚举这个添加的节点,在每一棵树上对于答案的贡献均为 $1$,单个节点的总贡献为 $f_{n-1}$,共计 $n$ 的节点的总贡献即 $n\cdot f_{n-1}$。

故,有:

\[g_n=n\cdot f_{n-1}=\dfrac{n}{2n-1}\dbinom{2n-2}{n-1}\]

答案即:

\[\begin{aligned} \dfrac{g_n}{f_n}&=\dfrac{\dfrac{n}{2n-1}\dbinom{2n-2}{n-1}}{\dfrac{1}{2n+1}\dbinom{2n}{2n}}\\ &=\dfrac{n(n+1)}{2(2n-1)} \end{aligned}\]

AC 代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
//#include<bits/stdc++.h>
#include<algorithm> 
#include<iostream>
#include<cstring>
#include<iomanip>
#include<cstdio>
#include<string>
#include<vector>
#include<cmath>
#include<ctime>
#include<deque>
#include<queue>
#include<stack>
#include<list>
using namespace std;
int n;
long double f(long long n){
	return n*(n+1)*1.0/(4*n-2);
}
int main(){
	/*freopen("test.in","r",stdin);
	freopen("test.out","w",stdout);*/
	
	ios::sync_with_stdio(false);
	cin.tie(0);cout.tie(0);
	
	cin>>n;
	cout.precision(9);
	cout.setf(ios::fixed);
	cout<<f(n)<<'\n';
	
	cout.flush(); 
	
	/*fclose(stdin);
	fclose(stdout);*/
	return 0;
}