卡特兰数

来源:互联网 发布:淘宝清除缓存 编辑:程序博客网 时间:2024/04/28 10:02
 卡特兰数学习总结

 

前言:

今天发现了一道题,认认真真的以为是组合数,认认真真的看了半天,认认真真的懵逼了好久,最后去看了题解,原来这个玩意儿是卡特兰数······

 

内容:

卡特兰数的数值h(n)1 ~ nn个数,按照从小到大的顺序入栈,不同的出栈序列种数。

 

公式:

1h(i) = c(2n, n) - c(2n, n - 1)

证明:我们来形象一点考虑这个序列的意义,我们把入栈看做I,出栈看做O,那么相当于我们现在有一个长度为2 * nIO序列,并且对于这个序列的每一个前缀都应该满足I的个数大于O的个数,(入栈数大于等于出栈数),那么首先,我们先不考虑后一个前缀中的个数限制,那么总的In个,On个的情况一定是C(2n,n)个,然后考虑,非法的情况,首先我们还是保证这个非法的序列有nInO,那么显然的,因为它是非法的,那么一定存在某个位置x,满足x是一个奇数,并且是第一个满足序列1 ~ x中的O的个数比I的个数大1的位置,那么考虑这种情况意味着什么,如果,我们将1 ~ x中的I全部变成OO全部变成I,那么原来的序列,会变成一个有着n +1I,有n - 1O的序列,并且显然,任意不同的非法序列,都一定能对应不同的有着n - 1On + 1I的序列,我们在来反向考虑,这样的序列是不是也一定能够对应一个非法序列,显然对于一个满足有n - 1On + 1I的序列,我们一定能找到一个位置x,满足x为奇数,并且,x是第一个满足1 ~ x中,I的数量比O的数量多1的(显然I的总数大于O的总数),那么我们将这一段中的IO反转,就形成了一个有着nOnI的非法序列,所以说最终的合法的序列种数为C(2n,n) - C(2n, n - 1)个,得证。

 

2

证明:我们还是在定义的基础上来搞一搞,设我们最后出栈的数为k,那么k -1一定在k入栈之前就已经出栈,也就是说k入栈的时候,栈为空,那么1 ~ k- 1出栈序列有h(k- 1)中,同样的,因为,k为最后出栈的,所以在k入栈之后,k + 1 ~ n全部入栈并且出栈之后,k最后出栈,那么k +1 ~ n的出栈序列有h(n- k)种,那么我们枚举k就可以获得上面的表达式了。

 

3h(n) = c(2n, n) / (n + 1)

证明:我们知道,c(2n,n) * n / (n + 1) = c(2n, n - 1),那么显然,从递推式一可以得到,h(n) = c(2n, n) - c(2n, n) * n / (n + 1) = c(2n, n) / (n + 1)

 

4h(n) = h(n - 1) * (4 * n - 2) / (n + 1)

证明:比较形象的证明我是没有找到的,但是可以直接粗暴的从式子1恒等证明回来,所以姑且算作是纯数学推导吧,具体的只需要证明c(2n - 2, n - 1) * (4 * n - 2) = c(2n, n)就可以了。

 

例题:有趣的数列

题目背景:

bzoj1485

分析:卡特兰数 + 取膜

一共2 * n个数,n个奇数项,n个偶数项,一个长度为2 * n1 ~ 2 * n的序列,每一个前缀中的奇数项一定大于等于偶数项,那么也就是可以想成是n个数进栈,求出栈序列,也就是卡特兰数了,然后我们要求的就是c(2n, n) - c(2n, n - 1)直接暴力分解质因数就可以了,复杂度O(nlogn)

Source

/*created by scarlyw*/#include <cstdio>#include <string>#include <algorithm>#include <cstring>#include <iostream>#include <cmath>#include <cctype>#include <vector>#include <set>#include <queue>const int MAXN = 1000000 + 10;int n, mod, prime_cnt;int up[MAXN], down[MAXN], prime[MAXN];bool not_prime[MAXN];inline void seive(int n) {not_prime[1] = true;for (int i = 2; i <= n; ++i) {if (!not_prime[i]) prime[++prime_cnt] = i;for (int j = 1; j <= prime_cnt && prime[j] * i <= n; ++j) {not_prime[i * prime[j]] = true;if (i % prime[j] == 0) break;}}}inline int solve(int n, int m) {static int cnt[MAXN];for (int i = 1; i <= m; ++i) down[i] = i, up[i] = n - i + 1;for (int i = 1; i <= prime_cnt; ++i) {int x = prime[i];for (int j = x; j <= m; j += x)while (down[j] % x == 0) cnt[x]++, down[j] /= x;for (int j = n % x + 1; cnt[x]; j += x) while (up[j] % x == 0 && cnt[x]) cnt[x]--, up[j] /= x;}int ans = 1;for (int i = 1; i <= m; ++i) ans = (long long)ans * (long long)up[i] % mod;return ans;}int main() {scanf("%d%d", &n, &mod), seive(n);printf("%d", (solve(2 * n, n) - solve(2 * n, n - 1) + mod) % mod);return 0;}