原文鏈接
https://www.luogu.org/blog/user29936/solution-p1641
題目鏈接
https://www.luogu.org/problem/P1641
題目描述
lxhgww最近接到了一個生成字符串的任務,任務需要他把n個1和m個0組成字符串,但是任務還要求在組成的字符串中,在任意的前k個字符中,1的個數不能少於0的個數。現在lxhgww想要知道滿足要求的字符串共有多少個,聰明的程序員們,你們能幫助他嗎?
輸入格式
輸入數據是一行,包括2個數字n和m
輸出格式
輸出數據是一行,包括1個數字,表示滿足要求的字符串數目,這個數可能會很大,只需輸出這個數除以20100403的餘數
輸入輸出樣例
輸入 #1
2 2
輸出 #1
2
說明/提示
limitation
每點2秒
對於30%的數據,保證1<=m<=n<=1000
對於100%的數據,保證1<=m<=n<=1000000
來源:SCOI 2010
題解
可以考慮把11的個數與00的個數的和看成xx座標,11的個數與00的個數的差看成yy座標,那麼如下圖:
向右上走(xx座標加11,yy座標加11)就表示這個字符選擇11。
向右下走(xx座標加11,yy座標減11)就表示這個字符選擇00。
這樣子,如果不考慮限制條件,就表示從(0,0)(0,0)走n+mn+m步到達(n+m,n-m)(n+m,n−m),這相當於從n+mn+m步中選出mm步向右下走,也就是C(n+m,m)C(n+m,m)。
考慮限制條件,任意前綴中11的個數不少於00的個數,也就是這條路徑不能經過直線y=-1y=−1。可以通過對稱性發現,從(0,0)(0,0)走到直線y=-1y=−1上的一點,相當於從(0,-2)(0,−2)走到該點。也就是說,路徑經過直線y=-1y=−1的方案數就是從(0,-2)(0,−2)走n+mn+m步到達(n+m,n-m)(n+m,n−m),這個方案數可以用組合數表示爲C(n+m,m-1)C(n+m,m−1)。
所以最後結果爲C(n+m,m)-C(n+m,m-1)C(n+m,m)−C(n+m,m−1)。對於組合數,可以預處理階乘後用乘法逆元計算。
代碼
#include <bits/stdc++.h>
#define maxn 1000010
#define ll long long
using namespace std;
const ll mod=20100403;
ll ni[maxn*2],n,m;
void init(){
ll i;
memset(ni,0,sizeof(ni));
ni[1]=1;
for(i=2;i<n+m+1;i++)ni[i]=-ni[mod%i]*(mod/i)%mod+mod;
}
int main(){
ll i,a1,a2;
cin>>n>>m;
init();
a1=1;
for(i=1;i<=n+m;i++){
a1*=i;
a1%=mod;
}
for(i=1;i<=n;i++){
a1*=ni[i];
a1%=mod;
}
for(i=1;i<=m;i++){
a1*=ni[i];
a1%=mod;
}
a2=a1;
a2=((a2*ni[n+1])%mod*m)%mod;
cout<<(a1-a2+mod)%mod;
return 0;
}