应用
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个球的可能数
解析:
分成两部分,
- 进m-1个,出m-k个 –>
catalan(m-1,m-k)
- 已有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;
}