BZOJ-1856 [Scoi2010]字串(卡特蘭數)
阿新 • • 發佈:2020-12-07
題目描述
把 \(n(1\leq n\leq 10^6)\) 個 \(1\) 和 \(m\) 個 \(0(1\leq m\leq 10^6)\) 組成字串,在字串所有字首中,\(1\) 的個數不少於 \(0\) 的個數,求滿足要求的字串有多少個。
分析
經典題。
設平面上的點 \((x,y)\) 的含義為已經有 \(x\) 個字元 \(1\),\(y\) 個字元 \(0\) ,顯然直線 \(y=x\) 上方的點都不合法。
一開始在點 \((0,0)\),終點為點 \((n,m)\)。每次可以向右走一格(放一個字元 \(1\))或者向上走一格(放一個字元 \(0\) 元的人)。可以發現,這就是點 \((0,0)\)
合法方案數 \(=\) 總方案數 \(-\) 不合法方案數。
任意一種不合法的方案數都至少與直線 \(y=x+1\) 有一個交點,設交點為 \(D\),並將路徑 \(B\rightarrow D\) 關於 \(y=x+1\) 翻折得到路徑 \(C\rightarrow D\)。
可以發現每一條從 \(B\rightarrow A\) 的不合法路徑都唯一對應著從 \(C\rightarrow A\) 的路徑。
\(C\) 的座標為 \((-1,1)\)。
則合法方案數為 \(\dbinom{n+m}{m}-\dbinom{n+m}{m-1}\)
程式碼
#include<bits/stdc++.h> using namespace std; const int mod=20100403; long long quick_pow(long long a,long long b) { long long ans=1; while(b) { if(b&1) ans=ans*a%mod; a=a*a%mod; b>>=1; } return ans; } const int N=2e6+10; long long fac[N+10],inv[N+10]; void init() { fac[0]=1; for(int i=1;i<=N;i++) fac[i]=fac[i-1]*i%mod; inv[N]=quick_pow(fac[N],mod-2); for(int i=N-1;i>=1;i--) inv[i]=inv[i+1]*(i+1)%mod; } long long C(long long n,long long m) { if(m>n) return 0; return fac[n]*inv[n-m]%mod*inv[m]%mod; } int main() { init(); long long n,m; cin>>n>>m; cout<<(C(n+m,m)-C(n+m,m-1)+mod)%mod; return 0; }