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) 编辑 收藏 举报