參考博客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; }