洛谷P1641 [SCOI2010]生成字串 解題報告
題面
lxhgww最近接到了一個生成字串的任務,任務需要他把n個1和m個0組成字串,但是任務還要求在組成的字串中,在任意的前k個字元中,1的個數不能少於0的個數。現在lxhgww想要知道滿足要求的字串共有多少個,聰明的程式設計師們,你們能幫助他嗎?
輸入輸出格式
輸入格式:
輸入資料是一行,包括2個數字n和m
輸出格式:
輸出資料是一行,包括1個數字,表示滿足要求的字串數目,這個數可能會很大,只需輸出這個數除以20100403的餘數
輸入輸出樣例
輸入樣例#1: 2 2 輸出樣例#1: 2說明
對於30%的資料,保證1<=m<=n<=1000
對於100%的資料,保證1<=m<=n<=1000000
本題解主要為讓本蒟蒻理清思路,因此在敘述上可能會有些繁瑣orz
首先我們分析一下題目要求,用n個1和m和0組成字串,且截至到任意位置,1的個數一定大於等於0的個數。
我們考慮建立一個平面直角座標系,x座標軸表示1和0的個數和,y座標軸表示1和0的個數差。
因此只要選一個新的數字,就是向右走。若選擇0就是向右下(1的個數減去0的個數變少了一個),同理選擇1就是向右上走(1的個數減去0的個數增加)。
由於直接求合法方案數比較困難,所以先求出全部方案數再減去不合法方案數。
- 全部方案數:從(0,0)點走到(n+m,n-m)點的個數。即選擇了全部n+m個數字,1的個數比0的個數恰好多了n-m個。
即從n+m個數中選擇了n個1——C(n+m,n)或C(n+m,m)。
- 再考慮不合法方案數,任意一點的 1個數減0個數 小於零,即經過 y=-1 這條線的情況全部不合法。
利用等效替代法:
我們把不合法的情況在經過 y=-1 這條線之前的線以 y=-1 為對稱軸向下翻折。起點就變成了 (0,-2),終點仍然是 (n+m,n-m)。
再沿y軸向上平移2個單位,即從(0,0)走到(n+m,n-m+2),即選擇n+1個1,m-1個0。
故不合法方案數等於C(n+m,n+1)。
答案即 C(n+m,n) - C(n+m,n+1)。
程式碼如下
1 #include <bits/stdc++.h> 2 #define ll long long 3 using namespace std; 4 5 const int p=20100403; 6 int n,m; 7 const int N=1e6+5; 8 ll jc[N<<1]; 9 10 inline ll ksm(ll a,ll b) 11 { 12 ll ans=1; 13 while(b) 14 { 15 if(b&1) ans=(ans*a)%p; 16 a=(a*a)%p; 17 b>>=1; 18 } 19 return ans; 20 } 21 22 inline ll C(int a,int b) 23 { 24 return jc[a]*ksm(jc[a-b]*jc[b]%p,p-2)%p; 25 } 26 27 int main() 28 { 29 scanf("%d%d",&n,&m); 30 jc[1]=1; 31 for(int i=2;i<=m+n;i++) 32 { 33 jc[i]=jc[i-1]*i%p; 34 } 35 cout<<(C(n+m,m)-C(n+m,m-1)+p)%p; 36 }View Code
#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int p=20100403;
int n,m;
const int N=1e6+5;
ll jc[N<<1];
inline ll ksm(ll a,ll b)
{
ll ans=1;
while(b)
{
if(b&1) ans=(ans*a)%p;
a=(a*a)%p;
b>>=1;
}
return ans;
}
inline ll C(int a,int b)
{
return jc[a]*ksm(jc[a-b]*jc[b]%p,p-2)%p;
}
int main()
{
scanf("%d%d",&n,&m);
jc[1]=1;
for(int i=2;i<=m+n;i++)
{
jc[i]=jc[i-1]*i%p;
}
cout<<(C(n+m,m)-C(n+m,m-1)+p)%p;
}