Description
时限:2s
lxhgww最近接到了一个生成字符串的任务,任务需要他把n个1和m个0组成字符串,但是任务还要求在组成的字符串中,在任意的前k个字符中,1的个数不能少于0的个数。现在lxhgww想要知道满足要求的字符串共有多少个,聪明的程序员们,你们能帮助他吗?
Input
输入数据是一行,包括2个数字n和m
Output
输出数据是一行,包括1个数字,表示满足要求的字符串数目,这个数可能会很大,只需输出这个数除以20100403的余数
Sample Input
2 2
Sample Output
2
Hint
对于30%的数据,保证1<=m<=n<=1000
对于100%的数据,保证1<=m<=n<=1000000
【分析】
这道题跟《奥赛经典-奥林匹克数学中的组合问题》31页的题很是相似。
。。。
我们用一个N+M维向量(a1,a2,a3...a(N+M))来表示这个字符串的组成。
我们从左往右依次填入数字0和1,当第k次填入时:
若填入1,则取ak=1
若填入0,则取ak=-1
我们令bk=a1+a2+...+ak=sigma(ak)
在平面直角坐标系中从左往右依次连接以下格点(1,b1),(2,b2),(3,b3)...(k,bk)...(N+M,b(N+M)),得到一条含N+M-1节的折线,因为1的个数不小于0的个数,故bk>=0(k=1,2...N+M),且b1=1,b(N+M)=N-M。
可见折线是一条连接(1,1)与(N+M,N-M),且与直线y=-1没有公共点的折线。
首先总的方案数为
A=C(N+M-1,(1/2)*((N+M-1)+(N-M-1)))=C(N+M-1,N-1)
上升+下降=(N+M)-1
上升-下降=(N-M)-1
(从总的长度中选择需要的上升长度)
然后我们将格点(1,1)以直线y=-1为对称轴对称的点记为p点,p点坐标显然为(1,-3)。
那么不满足与直线y=-1没有公共点的折线的数量,就等价于p点与(N+M,N-M)间折线的方案数。
即为
B=C(N+M-1,(1/2)*((N+M-1)+(N-M-(-3))))=C(N+M-1,(1/2)*((N+M-1)+(N-M+3)))=C(N+M-1,N+1)
最后答案就是A-B,由于N和M很小,所以直接暴力求解计算出分子分母模20100403的值,然后求一个乘法逆元即可。
另:
A-B=C(m+n-1,n-1)-C(m+n-1,n+1)
=C(n+m,n)*(n/(n+m))-C(n+m,n)*(m*(m-1)/(n+m)/n+1)
=C(n+m,n)*((n-m+1)/(n+1))
【代码】
/***********************
ID:Ciocio
LANG:C++
DATE:2014-1-20
TASK:string
************************/
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <iostream>
#include <algorithm>
#include <queue>
using namespace std;
#define MODER 20100403
typedef long long LL;
LL N,M;
void _init()
{
cin>>N>>M;
}
void _gcd(LL a,LL b,LL &d,LL &x,LL &y)
{
if(!b){d=a;x=1;y=0;}
else{_gcd(b,a%b,d,y,x);y-=x*(a/b);}
}
LL _inv(LL a,LL n)
{
LL d,x,y;
_gcd(a,n,d,x,y);
return d==1?(x+n)%n:-1;
}
void _solve()
{
LL fz,fm;
fz=N-M+1;
fm=N+1;
for(int i=2;i<=N+M;i++) fz=(fz*i)%MODER;
for(int i=2;i<=N;i++) fm=(fm*i)%MODER;
for(int i=2;i<=M;i++) fm=(fm*i)%MODER;
LL fm_1=_inv(fm,MODER);
LL ans=fz*fm_1%MODER;
cout<<ans<<endl;
}
int main()
{
_init();
_solve();
return 0;
}