卡特兰数

应用

 1.n个数进栈,有h(n)种出栈方式;
 2.凸n边形,用多条不相交的对角线分成三角形,有h(n-2)种可能性;
 3.n个节点,有h(n)种不同的二叉搜索树
 4.给n对括号排序,有h(n)种不同的正确的排序方式
 5.买票找零n个50元,m个100元(一开始柜台没零钱)
 6.n*n棋盘从左下角走到右上角而不穿过主对角线的走法
 7.矩阵连乘的括号化
 8.在圆上选择2n个点,将这些点成对连接起来使得所得到的n条线段不相交的方法数
 9.9.n层阶梯矩阵切割成n个长度递增矩阵的切割法

公式

  • 递推关系: h(n)=h(n-1)*(4*n-2)/(n+1);
  • 通项公式①: h(n)=C(2n,n)/(n+1) (n=0,1,2,…)
  • 通项公式②:h(n)=C(2n,n)-C(2n,n-1)(n=0,1,2,…)
  • 扩展公式:h(n,m)=C(n+m,n)-C(n+m,n+1)

扩展公式应用于进出不同(左右括号数不同)时,(n代表必须先有的项数量,n>m,如左括号,入栈)

例题

原题 2018年长沙理工大学第十三届程序设计竞赛 J 杯子

题意

n个球顺序入栈出栈,求第m个球进栈时栈内有k个球的可能数

解析

分成两部分,

  1. 进m-1个,出m-k个 –> catalan(m-1,m-k)
  2. 已有k个,进n-m个,出n-m+k个 –> (n-m+k,n-m)

这道题对于当前任意一个元素之前的操作,一定是进栈大于等于出栈,对于当前元素后边的元素则一定是出栈大于等于进栈。
ans=C(n+m,n)-C(n+m,n+1)是计算卡特兰数的公式,可以理解为n个0,m个1,排列保证前i个1出现个数多于0(1<=i<=n+m)。根据上述条件进栈和出栈的代表的01意义交换了。

代码

//卡特兰数
/*   应用
     1.n个数进栈,有h(n)种出栈方式;
     2.凸n边形,用多条不相交的对角线分成三角形,有h(n-2)种可能性;
     3.n个节点,有h(n)种不同的二叉搜索树
     4.给n对括号排序,有h(n)种不同的正确的排序方式
     5.买票找零n个50元,m个100元(一开始柜台没零钱)
     6.n*n棋盘从左下角走到右上角而不穿过主对角线的走法
     7.矩阵连乘的括号化
     8.在圆上选择2n个点,将这些点成对连接起来使得所得到的n条线段不相交的方法数
     9.n层阶梯矩阵切割成n个长度递增矩阵的切割法
*/
/*
递推关系:  h(n)=h(n-1)*(4*n-2)/(n+1);

通项公式:  h(n)=C(2n,n)/(n+1) (n=0,1,2,...)
            h(n)=C(2n,n)-C(2n,n-1)(n=0,1,2,...)

进出不同(左右括号数不同)时:  h(n,m)=C(n+m,n)-C(n+m,n+1) 
(n代表必须先有的项数量,n>m,如左括号,入栈)
*/

#include<iostream>
#include<cstdio>
#include<cmath>
#include<string>
#include<cstring>
#include<algorithm>
#include<set>
#include<map>
#include<list>
#include<vector>
#include<stack>
#include<queue>
#include<ctime>
#include<cstdlib>
#include<sstream>
#include<functional>
#define D long long
#define F double
#define MAX 0x7fffffff
#define MIN -0x7fffffff
#define mmm(a,b) memset(a,b,sizeof(a))
#define for1(i,a,b) for(int i=a;i<=b;i++)
#define for2(i,a,b) for(int i=a;i>=b;i--)
using namespace std;
#define N 1001000
#define MOD (D)((int)1e9+7)
#define mod (D)((int)1e9+7)
const double pi=acos(-1);
const F eps=1e-6;
D read(){ D ans=0; char last=' ',ch=getchar();
while(ch<'0' || ch>'9')last=ch,ch=getchar();
while(ch>='0' && ch<='9')ans=ans*10+ch-'0',ch=getchar();
if(last=='-')ans=-ans; return ans;
}
//**************************************************************
//基础公式
D swift(D a,D b){
    D ans=1ll;
    while(b){
        if(b%2)ans=ans*a%mod;
        b>>=1;
        a=a*a%mod;
    }return ans;
}
D inv(D a){return swift(a,mod-2);}
D fac[N*3];
void init_fac(){
    fac[0]=fac[1]=1ll;
    for(int i=2;i<=2*N;i++)fac[i]=fac[i-1]*i%mod;
}
D C(D a,D b){//组合数
    if(b>a||b<0)return 0;
    return fac[a]*inv(fac[b])%mod*inv(fac[a-b])%mod;
}
//**************************************************************
//求和求法
D h[N];
void init(){
    h[0]=h[1]=1;
    for(int i=2;i<=N;i++){
        for(int j=0;j<i;j++){
            h[i]+=h[j]*h[i-j-1];
            h[i]%=mod;
        }
    }
}
//**************************************************************
//公式法
D catalan(D n){
    return (C(2*n,n)-C(2*n,n-1)+mod)%mod;
}
D catalan(D n,D m){//m为大者
    return (C(n+m,n)-C(n+m,n+1)+mod)%mod;
}
//**************************************************************

int main(){
    init_fac();
    int t=read();while(t--){
        D n=read(),m=read(),k=read();
        printf("%lld\n",catalan(m-1,m-k)*catalan(n-m+k,n-m)%mod);
    }
    return 0;
}

阅读更多

扫码向博主提问

jk_chen_acmer

非学,无以致疑;非问,无以广识
去开通我的Chat快问
想对作者说点什么?

博主推荐

换一批

没有更多推荐了,返回首页