1. 程式人生 > 實用技巧 >BZOJ-1856 [Scoi2010]字串(卡特蘭數)

BZOJ-1856 [Scoi2010]字串(卡特蘭數)

題目描述

  把 \(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)\)

到點 $(n,m) $ 且不經過直線 \(y=x\) 上方的最短路徑數。

  合法方案數 \(=\) 總方案數 \(-\) 不合法方案數。

  任意一種不合法的方案數都至少與直線 \(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;
}