Catalan数

概要

(Catalan)数的递推式:

[C_{n+1}=sum_{i=0}^{n}C_icdot C_{n-i} ag{1} ]

[C_n=C_{n-1}cdotfrac{4n-2}{n+1} ag{2} ]

(Catalan)数的递推解(通项式):

[C_n=frac{egin{pmatrix} 2n \ n \ end{pmatrix} }{n+1} ag{3} ]

[C_n= egin{pmatrix} 2n \ n \ end{pmatrix} -egin{pmatrix} 2n \ n-1 \ end{pmatrix} ag{4} ]

(Catalan)数的前五项:(1,1,2,5,14)

(Catalan)数在(OEIS)上:A000108

应用

没有什么用的东西说完了,我们来讨论它的应用:

合法路径:从((0,0))((n,n)),只能向右或向上且不能越过(y=x),求路径数。

证明:不考虑不越过(y=x)的限制,方案数为(egin{pmatrix} 2n \ n \ end{pmatrix}),越过(y=x)的方案数等价于((-1,1))((n,n))的方案数(路径沿(y=x+1)对称),为(egin{pmatrix} 2n \ n-1 \ end{pmatrix}),与((4))式相同。

合法括号序列(n)个左括号,(n)个右括号,求组成的合法括号序列。原题

与合法路径等价。

出栈序列:进栈序为(1,2,dots,n),求出栈序计数。原题

进栈为左括号,出栈为右括号,即与合法括号序列等价。

二叉树计数:给定(n)个节点,求不同的二叉树种数。

左儿子为左括号,右儿子为右括号,考虑(dfs)序,与合法括号等价。

凸多边形划分:将一个凸多边形连接若干不相交的对角线划分的方案数。((C_{n-2}))

容易看出递推形式与((1))相同。

扩展(Catalan)(n)(01)串,(m)(0),前缀(1)的个数不少于(0),求计数。原题

[F_{n,m}=egin{pmatrix} n \ m \ end{pmatrix} -egin{pmatrix} n \ m-1 \ end{pmatrix} ag{5} ]

和合法路径的证明方式基本一致,即由((0,0))((n-m, m))的路径数减去((-1,1))((n-m, m))的路径数。

例题

然后就是一些很裸的例题。

(AHOI2012) 树屋阶梯

答案是(Catalan)数,可以由手推前四项基本得出。

如何推导?对于大小为(n)的阶梯,在上面切掉一个大小为(i(iin [0,n])的阶梯,在右边切掉一个大小为(n-i+1)的阶梯,中间放一个矩形,一共为(i+(n-i+1)+1=n)个矩形。不难发现这是((1))式。

可是我们需要高精度,我不想写高精怎么办?

于是就有了下面这份代码:

n=int(input())
ans=1
for i in range(n+2, n*2+1):
    ans*=i
for i in range(1, n+1):
    ans//=i
print(ans)

(python)大法好!

(HNOI2009) 有趣的数列

这个题应该是最有趣的一个。

首先打表(或手推)找规律肯定是一种省时省力的方法。

考虑推导:把(1-2n)依次放入数列,每次放在奇数位或偶数位的最前面一个。任何时候奇数位个数不少于偶数位,等价于合法括号。

但这个题还有一个问题:模数不为质数。这意味着我们不能常规地求逆元得到答案。

考虑质因数分解,因为(C_n=frac{prod_{i=n+2}^{2n}}{prod_{i=1}^{n}}),于是可以这么写。

	//线筛b[i]
	rep(i, 1, n) cnt[i]=-1;//分母
    rep(i, n+2, n<<1) cnt[i]=1;//分子
    per(i, n<<1, 2) if (b[i]<i) //b[i]为i的一个质因数
        cnt[b[i]]+=cnt[i], cnt[i/b[i]]+=cnt[i];
    rep(i, 2, n<<1) if (b[i]==i) ans=1ll*ans*Pow(i, cnt[i])%P;

复杂度是什么?预处理线筛(O(n)),分解(O(n)),统计答案(O(pi (n)cdot log(n))approx O(frac{n}{ln(n)}cdot log(n))approx O(n)),所以总复杂度为(O(n))

完整代码:

#include<cstdio>
#define rep(i, a, b) for (register int i=(a); i<=(b); ++i)
#define per(i, a, b) for (register int i=(a); i>=(b); --i)
using namespace std;
const int N=2000005;
int p[N], b[N], cnt[N], n, P, ans=1, tot;

inline int read()
{
 	int x=0,f=1;char ch=getchar();
    for (;ch<'0'||ch>'9';ch=getchar()) if (ch=='-') f=-1;
    for (;ch>='0'&&ch<='9';ch=getchar()) x=(x<<1)+(x<<3)+ch-'0';
    return x*f;
}

int Pow(int x, int t)
{
    int res=1;
    for (; t; t>>=1, x=1ll*x*x%P) if (t&1) res=1ll*res*x%P;
    return res;	
}

int main()
{
    n=read(); P=read(); 
    rep(i, 2, n<<1)
    {
        if (!b[i]) p[++tot]=i, b[i]=i;
        for (int j=1; j<=tot && p[j]*i<=n<<1; j++)
        {
            b[p[j]*i]=p[j];
            if (!(i%p[j])) break;
        }
    }
    rep(i, 1, n) cnt[i]=-1;
    rep(i, n+2, n<<1) cnt[i]=1;
    per(i, n<<1, 2) if (b[i]<i) 
        cnt[b[i]]+=cnt[i], cnt[i/b[i]]+=cnt[i];
    rep(i, 2, n<<1) if (b[i]==i) ans=1ll*ans*Pow(i, cnt[i])%P;
    printf("%d
", ans);
    return 0;
}

部分摘自百度百科:卡特兰数

原文地址:https://www.cnblogs.com/ACMSN/p/10798608.html