zoukankan      html  css  js  c++  java
  • 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;
    }
    

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

  • 相关阅读:
    如何修补软件、系统漏洞?
    轻松学习Linux之本地安装系统
    看程序体验缓冲区溢出漏洞
    企业网管软件实战之SolarWinds LANsurveyor
    Android项目开发遇到的问题(64K的错误)的解决之路,从入坑到出坑
    史上最佳 Mac+PhpStorm+XAMPP+Xdebug 集成开发和断点调试环境的配置
    [noip2011]计算系数+二项式定理证明
    [nowcoder5668H]Sort the Strings Revision
    (动态规划)导弹防御
    nyoj 79 拦截导弹
  • 原文地址:https://www.cnblogs.com/ACMSN/p/10798608.html
Copyright © 2011-2022 走看看