先上一个比较快O(n)的模板,n的范围是1e6
#include <iostream> #include <cmath> #include <ctime> #include <cstdio> #include <algorithm> #define ULL unsigned long long using namespace std; long long n; const long long M=1000000007; long long inv[1000010]; long long last,now=1; void init() { inv[1]=1; for(int i=2;i<=n+1;i++)inv[i]=(M-M/i)*inv[M%i]%M; } int main() { scanf("%lld",&n); init(); for(int i=2;i<=n;i++) { last=now; now=last*(4*i-2)%M*inv[i+1]%M; } printf("%lld\n",last); return 0; }下面来说它可以求什么样的问题:
1)出栈次序问题
1】n个数有多少种不同的进出栈顺序?
2】n辆火车有多少种不同进出站顺序?
2)加括号问题
1】n对括号有多少种匹配方式?
2】n+1个数相乘,有多少种乘法顺序?(要加n对括号)
3)二叉树问题
1】给定n个节点,能构成多少种不同的二叉树?
1】n个数有多少种不同的进出栈顺序?
2】n辆火车有多少种不同进出站顺序?
2)加括号问题
1】n对括号有多少种匹配方式?
2】n+1个数相乘,有多少种乘法顺序?(要加n对括号)
3)二叉树问题
1】给定n个节点,能构成多少种不同的二叉树?
转化思路:二叉树的递归问题直接满足catalan数的递推公式。
4)排队顺序问题
1】长度为2n的Dyck words有多少种?(Dyck words是由n个X和n个Y组成的字符串,其有一个特点:从左往右,对X和Y分别计数,Y的个数始终不大于X的个数。)
转化思路: 可以把X看作入栈,Y看作出栈,Y的个数始终不大于X的个数这一性质正好和空栈无法再出栈相一致,所以Dyck words就等价为出入栈序列。
1.1】2n个高矮不同的人,排成两排,每排必须是从矮到高排列,而且第二排比对应的第一排的人高,问排列方式有多少种?
转化思路: 2n个人先从矮到高排序,然后挨个被选到第一排或者第二排,如果要满足题意,当且仅当每挑选完一次之后,第二排中的人数不多于第一排中的人数。
1.2】在一个2*n的格子中填入1到2n这些数值,使得每个格子内的数值都比其右边和上边的所有数值都小,有多少种填法?
1.3】有2n个人排成一行进入剧场。入场费5元。其中n个人只有5元钞票,n个人只有10元钞票(必须找零),剧院无其它钞票,问有多少种买票顺序能让每个人都顺利买票?
转化思路: 每来一个5元的人,就能满足1个10元的人,所以始终要满足10元的人数不多于5元的人数。
5)正方形网格路径问题
1】对于一个n*n的正方形网格,每次我们能向右或者向上移动一格,求从左下角到右上角的所有在副对角线右下方的路径总数。
转化思路: 把一条水平边标记为+1,垂直边标记为-1,那么就组成一个n个+1和n个-1的序列,我们所要保证的是前k步中水平边的个数不小于垂直边的个数。
1.1】一位大城市的律师在他住所以北n个街区和以东n个街区处工作。每天她走2n个街区去上班。如果他从不穿越(但可以碰到)从家到办公室的对角线,那么有多少条可能的道路?
6)将多边形划分问题
1】n+2条边的凸多边形能划分成n个三角形,有多少种划分方法?
转化思路: 首先知道需要加n-1条弦。这里以边为研究对象,一共有n+2条边。加弦的目的是把两个挨着的边括起来。那么这就成了在两个边两侧加括号了,n+2条边要加n对括号。
2】n层阶梯划分为n个矩形,一共有多少种分法?
2】圆桌周围有2n个人,他们两两握手,但没有交叉的方案数。(每个人最多握一次手)
转化思路: 握手行为是由2个人共同完成的,设定这一行为有主动方(入栈)和被动方(出栈),假设握手行为是根据座次号(1-2n)按时间顺序发生的,如果那人是主动方,他会提出握手要求(+1);如果那人是被动方,他会接受握手要求(-1)。因为握手不能交叉,所以这就成了出入栈序列。
从没有交叉的图形角度来看,一旦确定了某个握手行为,握手的双方就划分了两个隔离的部分,这两个部分内部再进行握手。
2.1】在圆上选择2n个点,将这些点成对连接起来使得所得到的n条线段不相交的方法数。