Catalan(卡特兰)数
感觉这个定理很管用 可以解决很多问题 记录下来
递推公式
公式如下:
C
2
n
n
−
C
2
n
n
−
1
=
C
2
n
n
n
+
1
\mathbf{C} _{2n}^n - \mathbf{C} _{2n}^{n-1}= \frac{\mathbf{C} _{2n}^{n}}{n+1}
C2nn−C2nn−1=n+1C2nn
过程:
先引入一个问题 以图的视角解释更加简单。
例如 01序列
同等个数的0和1 满足任意前缀序列中0的个数不少于1的个数的方案数。
现有n个0和n个1 若取0 ----> 向右走 取1 ----> 向左走
则从 (0, 0) 走到 (n, n) 共有
C
2
n
n
\mathbf{C} _{2n}^n
C2nn种方案 包括 合法+不合法
含义是:从2n个点从挑选出来n个点右走或者上走
将横坐标看为x轴 纵坐标看为y轴。 可知 合法序列 要保证x>=y 就是对角线下方
而 但凡超过这个对角线界限 就变为了不合法的情况。
又根据对称可知 任何一个不合法的情况 都会到达 (n-1, n+1)的这个位置。
所以可以推出 不合法数: C 2 n n − 1 \mathbf{C} _{2n}^{n-1} C2nn−1
于是 合法方案=总方案-不合法方案 C 2 n n − C 2 n n − 1 = C 2 n n n + 1 \mathbf{C} _{2n}^n - \mathbf{C} _{2n}^{n-1}= \frac{\mathbf{C} _{2n}^{n}}{n+1} C2nn−C2nn−1=n+1C2nn
相似问题
- 你有n个左括号,n个右括号,问有多少个长度为2n的括号序列使得所有的括号都是合法的
- 有一个栈,我们有2n次操作,n次进栈,n次出栈,问有多少中合法的进出栈序列
- 一个长度为n的排列a,只要满足i<j<k且aj<ak<ai就称这个排列为312排列
求n的全排列中不是312排列的排列个数
例题代码
01序列代码
三种方法:
- 扩展欧几里得 时间最快
- 快速幂 代码简短
- 提前初始化 适合多案例
#include<iostream>
#include<algorithm>
using namespace std;
typedef long long ll;
const int mod=1e9+7;
int n;
//快速幂
int qmi(int a, int b, int p){
int res=1;
while(b){
if(b&1) res=(ll)res*a%p;
b>>=1;
a=(ll)a*a%p;
}
return res%p;
}
//扩展欧几里得
int exgcd(int a, int b, int &x, int &y){
if(!b){
x=1, y=0;
return a;
}
int d=exgcd(b, a%b, y, x);
y=y-(ll)a/b*x%mod;
return d;
}
//提前初始化 每个数的阶乘和阶乘的逆元
void init(){
f[1]=inf[1]=1;
for(int i=2;i<=N;i++){
f[i]=(ll)f[i-1]%mod*i%mod;
inf[i]=(ll)inf[i-1]%mod*qmi(i, mod-2, mod)%mod;
}
}
int main(){
cin>>n;
int a=2*n, b=n;
ll res=1;
for(int i=a;i>a-b;i--){
res=(ll)res*i%mod; //计算组合数的分母
}
int x, y;
for(int i=2;i<=b;i++) {
/* exgcd(i, mod, x, y); //x就是i的逆元
res=(ll)res*x%mod; //扩展欧几里得
*/
res=(ll)res*qmi(i, mod-2, mod)%mod; //快速幂
}
/*
exgcd(n+1, mod ,x, y); 扩展欧几里得
*/
res=(ll)res*qmi(n+1, mod-2, mod)%mod; //快速幂
/*
res=((ll)res*x%mod+mod)%mod; 扩展欧几里得
*/
/*
提前初始化
ll res=(ll)f[a]%mod*inf[b]%mod*inf[b]%mod*qmi(n+1, mod-2, mod)%mod;
*/
cout<<res<<endl;
return 0;
}