Catalan 數
引入
Problem:
具有n個節點的二叉樹的形態有多少種?
分析:
因為二叉樹定義具有遞歸性,左子樹有i個節點,那么右子樹有n-i-1個節點,根據計數原理:
\[f\left ( n \right )=\sum_{i=0}^{n-1}f\left ( i \right )\cdot f\left ( n-i-1 \right ),f\left ( 0 \right )=1,f\left ( 1 \right )=1 \]
Catalan數
這里的:
\[f\left ( n \right )=\sum_{i=0}^{n-1}f\left ( i \right )\cdot f\left ( n-i-1 \right ) \]
就叫做卡特蘭數,該數列的前若干項是:
1,2,5,14,42,132,429,1430....
所以增長速度是非常快的。
常見的Catalan數表達式
1、遞推式:
\[f\left ( n \right )=\sum_{i=0}^{n-1}f\left ( i \right )\cdot f\left ( n-i-1 \right ),f\left ( 0 \right )=1 \]
2、另類遞推式:
\[f\left ( n \right )=\frac{(4n-2)f\left ( n-1 \right )}{n+1} \]
3、通向式(重要,常用):
\[f\left ( n \right )=\frac{C\begin{matrix} n\\ 2n \end{matrix}}{n+1} \]
4、通向式2:
\[f\left ( n \right )=C\begin{matrix} n\\ 2n \end{matrix} -C \begin{matrix} n-1\\ 2n \end{matrix} \]
利用通向式求Catalan(n) Code
#define ll long long
ll Catalan(ll n){
ll ans=1;
for(ll i=n+1;i<=2*n;i++){
ans=ans*i/(i-n);
printf("%d %d %lld\n",i,i-n,ans);
}
return ans/(n+1);
}
應用:
•Cn表示n個節點不同形態的二叉樹個數。
•Cn表示n的入棧序列對應的合法出棧序列的個數。(luogu P1044)
變式:
•n個0和n個1,構造一個長度為2n的序列,使得序列的任意前綴中1的個數不 少於0的個數,這樣的序列有多少種?
•n個矩陣相乘,用括號改邊運算順序,有多少種?(ps:n個矩陣相乘需要n-1對括號,再增加是無意義的)
•游樂園門票1元一張,每人限購一張。現在有10個小朋友排隊購票,其中5個小朋友每人只有1元的鈔票一張,另5個小朋友每人只有2元的鈔票一張,售票員沒有准備零錢。問:有多少種排隊方法,使售票可以正常進行下去?
•凸多邊形分割成三角形方案數
圖像法
n·n的矩陣,每次只能往右或往上走1個單位,問從(0,0)走到n·n,且路線一直處於y=x之下的方法總數是多少?
只需要n步的向右和n步的向上就能到達(n,n)。為了不跨越y=x,需任意時刻向右的次數>=向上的次數,顯然答案就是卡特蘭數。
擴展:幾乎所有的卡特蘭數的問題都可以用這樣的折線法方式解答。比如入棧映射為向右,出棧映射成向上,任意時刻入棧次數>=出棧
例題-luogu P2532 [AHOI2012]樹屋階梯
Problem
分析
卡特蘭數,注意N的取值范圍,要用高精度。
Code
#include <cstdio>
#include <iostream>
#define ll long long
using namespace std;
const int maxn=10005;
struct highprecc{
int l,a[maxn];
void init(){l=1;a[1]=1;}
void out(){for(int i=l;i>=1;i--) printf("%d",a[i]);}
highprecc operator * (const ll b) const{
highprecc c;
for(int i=1;i<=l;i++) c.a[i]=a[i]*b;
for(int i=2;i<=l;i++){
c.a[i]+=c.a[i-1]/10;
c.a[i-1]%=10;
}
c.l=l;
while(c.a[c.l]>10){
c.a[c.l+1]=c.a[c.l]/10;
c.a[c.l]%=10;
c.l++;
}
return c;
}
highprecc operator / (const ll b) const{
highprecc c;
ll k=l,g=0;
for(int i=l;i>0;i--){
g=g*10+a[i];
c.a[i]=g/b;
g%=b;
}
while(k>1 && c.a[k]==0) k--;
c.l=k;
return c;
}
};
highprecc f(ll n){
highprecc ans;ans.init();
for(ll i=n+1;i<=2*n;i++) ans=ans*i/(i-n);
return ans/(n+1);
}
int main(){
ll n;scanf("%lld",&n);
f(n).out();
return 0;
}