卡特蘭數 洛谷P1641 [SCOI2010]生成字串

折翼的小鳥先生發表於2020-11-14

卡特蘭數

參考部落格

介紹

卡特蘭數為組合數學中的一種特殊數列,用於解決一類特殊問題

\(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;
}

相關文章