Catalan(卡特兰)数

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} C2nnC2nn1=n+1C2nn
过程:

先引入一个问题 以图的视角解释更加简单。

例如 01序列

同等个数的0和1 满足任意前缀序列中0的个数不少于1的个数的方案数。

img

现有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} C2nn1

于是 合法方案=总方案-不合法方案 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} C2nnC2nn1=n+1C2nn

相似问题

  1. 你有n个左括号,n个右括号,问有多少个长度为2n的括号序列使得所有的括号都是合法的
  2. 有一个栈,我们有2n次操作,n次进栈,n次出栈,问有多少中合法的进出栈序列
  3. 一个长度为n的排列a,只要满足i<j<k且aj<ak<ai就称这个排列为312排列
    求n的全排列中不是312排列的排列个数

例题代码

01序列代码

三种方法:

  1. 扩展欧几里得 时间最快
  2. 快速幂 代码简短
  3. 提前初始化 适合多案例
#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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值