[bzoj4001] [TJOI2015]概率论

本文介绍了一种计算有根二叉树叶子节点期望数量的方法,通过巧妙地利用卡特兰数和生成函数,推导出了简洁的数学公式,并提供了一个高效的C++实现。

Description

img

Input

输入一个正整数N,代表有根树的结点数

Output

输出这棵树期望的叶子节点数。要求误差小于1e-9

Sample Input

1

Sample Output

1.000000000

HINT

1<=N<=10^9

solution

神仙题,(思维难度适中,码量极低,解法自然)

\(a_n\)\(n\)个节点的二叉树的个数,\(b_n\)\(n\)个节点二叉树所有方案的叶子节点数之和,那么答案显然为\(a_n/b_n\)

那么枚举左右子树有多少节点显然可以得到:
\[ a_n=\sum_{i=0}^{n-1}a_ia_{n-i-1} \]
(众所周知)这就是卡特兰数,即:
\[ a_n=\frac{1}{n-1}\binom{2n}{n} \]
对于\(b_n\),同样枚举左右子树节点个数可得:
\[ b_n=\sum_{i=0}^{n-1}a_i*b_{n-i-1}+b_{i}*a_{n-i-1} \]

\[ b_n=2\sum_{i=0}^{n-1}a_ib_{n-i-1} \]
然后考虑这两个函数的生成函数,设:
\[ F(x)=\sum_{i=0}^{\infty}a_ix^i\\ G(x)=\sum_{i=0}^{\infty}b_ix^i\\ \]


然后注意到上面关于\(a\)的式子是一个卷积形式,构造生成函数的乘积:
\[ F^2(x)=\sum_{p=0}^\infty \sum_{i=0}^{p}a_ia_{p-i}x^p \]
\(x\)右移:
\[ \begin{align} &~~~~~xF^2(x)\\&=\sum_{p=1}^\infty \sum_{i=0}^{p-1}a_ia_{p-i-1}x^p \\&=\sum_{p=1}^\infty a_px^p \end{align} \]
补上第0项,得:
\[ F(x)=xF^2(x)+1 \]
对于\(F(x)\),解方程得:
\[ F(x)=\frac{1\pm \sqrt{1-4x}}{2x} \]
对于取正号时:
\[ \lim_{x\rightarrow0}F(x)=\infty \]
函数不收敛,舍去。

取负号时:
\[ \lim_{x\rightarrow0}F(x)=1 \]
所以
\[ F(x)=\frac{1- \sqrt{1-4x}}{2x} \]


同样,对于\(b_n\),也是一个卷积形式,得:
\[ \begin{align} &~~~~~2xG(x)F(x)\\&=\sum_{p=1}^\infty 2\sum_{i=0}^{p-1}a_ib_{p-i-1}x^p\\ &=\sum_{p=2}^\infty b_px^p \end{align} \]
注意这里\(p=1\)时后面的卷积为0,所以补上一个\(x\)得:
\[ G(x)=2xF(x)G(x)+x \]
解得:
\[ G(x)=\frac{x}{\sqrt{1-4x}} \]


然后注意到:
\[ (F(x)*x)^\prime=\frac{1}{\sqrt{1-4x}}=\frac{G(x)}{x} \]
对于\((F(x)*x)^\prime\),第\(n\)项为:
\[ a_nx^n\rightarrow a^nx^{n+1}\rightarrow (n+1)a^nx^n \]
对于\(G(x)/x\),第\(n+1\)项为:
\[ b_{n+1}x^{n+1}\rightarrow b_{n+1}x^n \]
所以比较系数可得:
\[ (n+1)a^nx^n=b_{n+1}x^n \]
即:
\[ na_{n-1}=b_n \]
考虑到
\[ a_n=\frac{1}{n+1}\binom{2n}{n} \]
所以答案为:
\[ \frac{b_n}{a_n}=\frac{na_{n-1}}{a_n}=\frac{n(n+1)}{2(2n-1)} \]
代码....读入输出小清新题

#include<bits/stdc++.h>
using namespace std;

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

void print(int x) {
    if(x<0) putchar('-'),x=-x;
    if(!x) return ;print(x/10),putchar(x%10+48);
}
void write(int x) {if(!x) putchar('0');else print(x);putchar('\n');}

int main() {
    int n;read(n);
    printf("%.9lf\n",(double)n*(n+1)/2/(2*n-1));
    return 0;
}

转载于:https://www.cnblogs.com/hbyer/p/10114734.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值