卡特兰数 洛谷P1641 [SCOI2010]生成字符串

卡特兰数

参考博客

介绍

卡特兰数为组合数学中的一种特殊数列,用于解决一类特殊问题

\(f(n)\)为卡特兰数的第n项

其通项公式为

\[f(n)=\frac{2n\choose n}{n+1} \]

关于它的证明

当然也有递推式

\[f(n)=\sum\limits_{i=0}^{n-1}f(i)\ast f(n-i-1) \]

最常用的则是对于通项的变形式

\[f(n)={2n\choose n}-{2n\choose n-1} \]

在此给出一较易的证明

例题

我们来看一道例题洛谷 p1641 生成字符串

比较模板的一道卡特兰数的例题,用上面给出的公式可以直接求解,我们对本题建模,假设m=n,我们建立一个

\(n*n\)的网格图,把0看作向上走一个单位,把1看作向右走一个单位,我们以\((0,0)\)为起点,\((n,n)\)为终点,

考虑到本题的限制,即在任意的前 k 个字符中,1 的个数不能少于 0 的个数,所以,每一个合法的路

径都不能越过该网格图的对角线,设直线\(l\)为将对角线向上平移一个单位所得到的直线,所有经过

\(l\)的路径都是非法路径,我们用所有路径数减去非法路径数就是合法的路径数,设\(x\)为一非法路径与

直线\(l\)的交点,对该路径\(x\)后的部分以\(l\)为对称轴对称过去,我们发现,所有非法路径对称后的

终点都为\((n-1,n+1)\)因为所有的对称后路径与先前的非法路径都是一一对应的,所以,非法路径个数

就是对称后路径个数,所以,用所有路径减去非法路径就是合法路径个

数,其实答案就是上面第三个公式。

对于\(m<=n\),同样的思路,只不过非法路径的终点与\(m=n\)不一样了,只需

要求出对称点,其余与上相同

如果不是很清楚,建议看一下第三个公式的证明的博客

代码

具体求组合数采用卢卡斯定理

注意,在遇到需要取模后输出的题目,算出的答案可能为负数,所以就需要+mod后%mod,本题如果不这样写的话只有70分
.

#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#define int long long
using namespace std;
const int maxn=3e6+10;
const int p=20100403;
int n,m;
int a[maxn];
int power(int x,int t)
{
    if(x==0) return 0;
	x%=p;
    int b=1;
    while(t)
    {
        if(t&1) b=b*x%p;
        x=x*x%p; t>>=1;
    }
    return b;
}
int cm(int a1,int b1){
	if(a1<b1)
		return 0;
	return (a[a1]*power(a[b1],p-2)%p)*power(a[a1-b1],p-2)%p;
}
int lucas(int n,int m,int p){
	if(!m){
		return 1;
	}
	return cm(n%p,m%p)*lucas(n/p,m/p,p)%p;
}
signed main(){
	ios::sync_with_stdio(false);
	cin>>n>>m;
	a[0]=1;
	for(int i=1;i<=n+m+10;i++){
		a[i]=(a[i-1]*i)%p; 
	}
	int nn=m-1;
	int mm=n-nn+m;//(nn,mm)为非法路径的终点
	cout<<(lucas(n+m,n,p)-lucas(nn+mm,nn,p)+p)%p;
	return 0;
}
posted @ 2020-11-14 21:49  折翼的小鸟先生  阅读(168)  评论(0编辑  收藏  举报