卡特兰数

题目

给定 n 个 0 和 n 个 1,它们按照某种顺序拍成长度为 2n 的序列,求它们能排成的所有序列中,能够满足任意前缀序列 0 的个数都不少于 1 的个数的序列有多少个。答案对 109+7 取模。
数据范围: 1≤n≤105


解法

(在下文叙述中,为了方便,满足题目要求的称为合法路径,不满足的称为不合法路径)

将01序列转化到坐标系上:
0 代表向右走一格
1 代表向上走一格

那么任意前缀序列 0 的个数都不少于 1 的个数的序列(合法路径),就可以转换为:路径上任意一点,都满足 x ≥ y

以 n = 6 为例:
以 n = 6 构成的每个 01 序列都可以转换成一条从 (0,0) 到 (6,6) 的路径
图中的橙色线即为一条不合法路径(因为存在点不满足 x ≥ y),只有一条路径的所有点都在红线以下,才算合法。
在这里插入图片描述
从这条路径与红线的第一个交点开始,关于红线为轴,将剩下路径做对称,如下图中绿色线段。
我们发现,从(0,0)到(6,6)的这条不合法路径(橙色)对应着一条从(0,0)到(5,7)的路径(绿色)。反过来,从(0,0)到(5,7)的这条路径对应着一条从(0,0)到(6,6)的不合法路径。

合法路径数 = 总路径数 - 不合法路径数 = C(12,6) - C(12,5) = 132

在这里插入图片描述
将上面结论拓展一下, 任何一条从(0,0)走到(n,n)的不合法路径,都对应一条从(0,0)到(n-1,n+1)的路径。反过来,任何一条从(0,0)走到(n-1,n+1)的路径,也都对应一条从(0,0)走到(n,n)的不合法路径。


卡特兰数推导

合法路径数
= 总路径数 - 不合法路径数

= C(2n,n) - C(2n,n-1)

= ( 2 n ) ! ( n ) ! ( n ) ! {{(2n)!} \over {(n)!(n)!}} (n)!(n)!(2n)! - ( 2 n ) ! ( n − 1 ) ! ( n + 1 ) ! {{(2n)!} \over {(n-1)!(n+1)!}} (n1)!(n+1)!(2n)!

= ( 2 n ) ! ( n + 1 ) − ( 2 n ) ! n ( n + 1 ) ! ( n ) ! {{(2n)!(n+1)-(2n)!n} \over {(n+1)!(n)!}} (n+1)!(n)!(2n)!(n+1)(2n)!n

= ( 2 n ) ! ( n + 1 ) ! ( n ) ! {{(2n)!} \over {(n+1)!(n)!}} (n+1)!(n)!(2n)!

= 1 n + 1 {{1} \over {n+1}} n+11 ( 2 n ) ! ( n ) ! ( n ) ! {{(2n)!} \over {(n)!(n)!}} (n)!(n)!(2n)!

= 1 n + 1 {{1} \over {n+1}} n+11C(2n,n)


代码

由于109 + 7是质数,所以可以用快速幂求逆元。快速幂求逆元讲解

#include<iostream>
using namespace std;
typedef long long LL;

const int mod = 1e9 + 7;

int qmi(int a,int k,int p)
{
	int res = 1;
	while(k)
	{
		if(k & 1) res = (LL)res * a % p;
		a = (LL)a * a % p;
		k >>= 1;
	}
	return res;
}

int main()
{
	int n;
	cin >> n;
	
	int a = 2 * n, b = n;
	int res = 1;
	
	for(int i=a; i>a-b; i--) res = (LL)res * i % mod;
	for(int i=1; i<=b; i++) res = (LL)res * qmi(i, mod-2, mod) % mod;
	
	res = (LL)res * qmi(n+1, mod-2, mod) % mod;
	
	cout << res;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值