题意:
给你n个1和m个0,让你拼成n+m长度的并且所有前缀的1都不少于0的个数的串,问有多少种方法。
思路:
将构造字符串的过程转化到二维坐标上去,1用y表示,0用x表示,从坐标(0,0)出发,0代表向右走(x增加),1代表向上走(y增加),因为0有m个,1有n个,所以最后到达的坐标为
(m,n) ,单纯考虑从0,0走到m,n一共有C(n+m,m)种方法,又因为任意前缀中1的个数不能小于0,所以y>=x,也就是合法走的路线经过的坐标要么在y=x上,要么在其上边,那么
不合法的路线经过的坐标则满足y<x,也就是路线不能与y=x-1相交,因为只要一相交,就不满足1的个数不能小于0的个数。
所以不合法的路径一定与y=x-1相交,找到(0,0)关于y=x-1的对称点(1,-1),从(1,-1)走到(m,n)一定与直线y=x-1相交,因为m,n在该直线的上边,1,-1在该直线的下
边。在这里设交点为P,那么路线就以P为分割点可以分为两部分,上面一部分取个名字叫不合法路线,下面一部分取个名字叫合法路线。那么从1,-1走到m,n有多少种方法也就
是从0,0走到m,n的不合法的方法数。仔细想一想为什么呢?为什么要找对称点,在对称直线一侧走的路线总可以在另一侧找到路线与之对称,刚才我们从1,-1走到m,n的那一段
合法路线沿着y=x-1再对称过去,不合法路线就不用对称了,因为不合法路线已经与y=x-1相交了(其实那一段合法路线的最后一个点也与y=x-1相交),这样就有了从0,0到m,n
的一种方案,总的来说就是对于从1,-1到m,n的每一条路线,都有从0,0到m,n关于y=x-1对称的一条路线与之对应。从1,-1走到m,n方法数为C (m+n,m-1)。
所以本题的答案就是C(n+m,m) - C (m+n,m-1)。
代码:
#include"cstdlib"
#include"cstdio"
#include"cstring"
#include"cmath"
#include"queue"
#include"algorithm"
#include"iostream"
#include"map"
#include"stack"
#define N 2000003
#define eps 1e-8
#define mod 20100501
using namespace std;
#define ll __int64
#define MAXN 20000007
bool mark[MAXN];
int ss[MAXN/3],sscnt,sum[MAXN/3];
void ssb()
{
sscnt=0;
memset(mark,false,sizeof(mark));
mark[0]=mark[1]=true;
for(int i=2; i<=MAXN; i++)
{
if(!mark[i])
{
for(int j=i+i; j<=MAXN; j+=i) mark[j]=true;
ss[sscnt++]=i;
}
}
return ;
}
ll power(ll a,ll b)
{
ll ans=1;
while(b)
{
if(b&1) ans=(ans*a)%mod;
a=(a*a)%mod;
b>>=1;
}
return ans;
}
ll yzs(ll x,ll y)
{
ll ans=x/y;
if(x<y) return ans;
return ans+=yzs(x/y,y);
}
ll fc(ll n,ll m)
{
if(n<m) return 0;
ll ans=1;
for(int i=0; i<sscnt; i++)
{
if(ss[i]>n) break;
ans*=power(ss[i],yzs(n,ss[i])-yzs(m,ss[i])-yzs(n-m,ss[i]));
ans%=mod;
}
return ans;
}
int main()
{
int t;
cin>>t;
ssb();
while(t--)
{
ll n,m;
scanf("%I64d%I64d",&n,&m);
ll a=1,b=1;
a=fc(n+m,n);
b=fc(n+m,n+1);
ll ans=((a-b)%mod+mod)%mod;
printf("%I64d\n",ans);
}
return 0;
}