组合数、坐标轴。
以\(n\)个\(1\)和\(m\)个\(0\)组成字符串,求出满足条件「在任意的前\(k\)个字符中,\(1\)的个数不能少于\(0\)的个数」的字符串数量。
考虑到题目要求的条件「\(1\)的个数不少于\(0\)的个数」可以转化为坐标轴内点的横纵坐标。
举例说明:
第一步:建系。将\(x\)轴设为\(m + n\),y轴设为\(m - n\),方案数则可以表示为从点\((0,0)\)移动到点\((n + m,n - m)\)的折线,每增加一个\(1\)或\(0\)代表当前点向\(x\)轴正半轴移动一单位长度,选择\(1\)代表向y轴正半轴移动一单位长度,选择\(0\)代表向\(y\)轴负半轴移动一单位长度。易得方案总数即为\(C(n+m,n)\)。
因为\(1\)的个数始终不少于\(0\)的个数,所以折线需保持在第一象限。
第二步:排除不合法情况:\(1\)的个数少于\(0\)的个数,即折线在第四象限。
我们考虑将在到达\(y = -1\)以前的部分全部沿\(y = -1\)的直线作对称,如下图所示。
折线的新起点变为\((0,-2)\),总次数还是\(n + m\),但上升次数比下降次数多一次,方案数为\(C(n+m,m-1)\)。
第三步:综合前两步的推导,得出式子:\(Ans = C(n + m,n) - C(n + m,m - 1)\)。
\(1\).注意取模。
\(2\).数据范围过大,先预处理逆元,再套用公式求出组合数。
#include<bits/stdc++.h> #define re register #define il inline #define ll long long #define MAXN 1000005 #define MAXM 1000005 #define MOD 20100403 #define rep(i,a,b) for(re int i = a;i <= b;i ++) #define drep(i,a,b) for(re int i = a;i >= b;i --) #define Rep(i,a,b) for(re int i = a;i < b;i ++) #define Drep(i,a,b) for(re int i = a;i > b;i --) #define fin(a) freopen(#a".in","r",stdin) #define fout(a) freopen(#a".out","w",stdout) using namespace std; il ll read(){ ll x = 0; char ch = 0; while(!isdigit(ch)){ ch = getchar(); } while(isdigit(ch)){ x = (x << 3) + (x << 1) + (ch ^ 48); ch = getchar(); } return x; } il void write(ll x){ if(x > 9){ write(x / 10); } putchar(x % 10 + '0'); } il ll Pow(ll a,ll b){ ll ans = 1; while(b){ if(b & 1) ans = ans * a % MOD; a = a * a % MOD; b >>= 1; } return ans % MOD; } il ll C(ll n,ll m){ if(m > n) return 0; if(m > n - m) m = n - m; ll s1 = 1,s2 = 1; Rep(i,0,m){ s1 = s1 * (n - i) % MOD; s2 = s2 * (i + 1) % MOD; } return s1 * Pow(s2,MOD - 2) % MOD; } il ll Lucas(int n,int m){ if(!m) return 1; return C(n % MOD,m % MOD) * Lucas(n / MOD,m / MOD) % MOD; } int main(){ #ifndef ONLINE_JUDGE fin(1641); fout(1641); #endif int n = read(),m = read(); write((Lucas(n + m,n) % MOD - Lucas(n + m,m - 1) % MOD + MOD) % MOD); return 0; }