DestinHistoire

 

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;
}

posted on 2020-12-07 21:53  DestinHistoire  阅读(51)  评论(0编辑  收藏  举报

导航