参考博客html
卡特兰数为组合数学中的一种特殊数列,用于解决一类特殊问题ios
设\(f(n)\)为卡特兰数的第n项c++
其通项公式为spa
关于它的证实.net
固然也有递推式code
最经常使用的则是对于通项的变形式htm
在此给出一较易的证实blog
咱们来看一道例题洛谷 p1641 生成字符串ci
比较模板的一道卡特兰数的例题,用上面给出的公式能够直接求解,咱们对本题建模,假设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; }