Description
给出两个整数m和n,要求用n个1和m个0构造一种串,这个串满足任意前缀1的数量不少于0的数量,问这种串有多少个
Input
第一行为一整数T表示用例组数,每组用例为两个正整数n和m
(T<=100, 1 <= m <= n <= 1000000 )
Output
对于每组用例,输出满足条件的串的个数,结果模20100501
Sample Input
1
2 2
Sample Output
2
Solution
将构造串的过程看作一个点从(0,0)到(n,m)的路径,1表示向右走,0表示向上走,方法数为C(n+m,n),任意前缀1数量不少于0即为这条路径不能与直线y=x+1相交,考虑与y=x+1相交的路径,由(0,0)关于y=x+1的对称点为(-1,1)及所有从(-1,1)到(n,m)的路径都与直线y=x+1相交知,所有从(0,0)到(n,m)且与直线y=x+1相交的路径都与从(-1,1)到(n,m)的路径一一对应,故不满足条件的方法数为C(n+m,n+1),故答案为ans=C(n+m,n)-C(n+m,n+1)=(n+m)!(n+1-m)/((n+1)!m!),因为n和m非常大,所以用质因子的加减代替阶乘的乘除,此处用到n!的质因子分解方法,对一个素数p,我们想知道其在n!的质因子分解表达式中的幂指数a,显然n!中含p的项有n/p项,含p^2的项有n/p/p,…,以此类推累加各项即得到a的值。
Code
#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long ll;
#define mod 20100501ll
#define maxm 2000011
#define maxn 155555
int p[maxn];
int prime[maxn],res,num;
bool is_prime[maxm];
void get_prime()
{
prime[0]=2;
res=1;
for(int i=3;i<maxm;i+=2)
if(!is_prime[i])
{
for(int j=i;j<maxm;j+=i)
is_prime[j]=1;
prime[res++]=i;
}
}
ll mod_pow(ll a,ll b,ll p)
{
ll ans=1ll;
a%=p;
while(b)
{
if(b&1) ans=(ans*a)%p;
a=(a*a)%p;
b>>=1;
}
return ans;
}
void divide(int n)//对n质因子分解
{
int cnt=0;
while(n>1)
{
while(n%prime[cnt]==0)
{
p[cnt]++;
n/=prime[cnt];
}
cnt++;
}
num=max(num,cnt);
}
void Divide(int n,bool state)//对n!质因子分解
{
int cnt=0,temp;
while(prime[cnt]<=n)
{
temp=n;
while(temp)
{
if(state) p[cnt]+=temp/prime[cnt];
else p[cnt]-=temp/prime[cnt];
temp/=prime[cnt];
}
cnt++;
}
num=max(num,cnt);
}
ll get_ans()
{
ll ans=1ll;
for(int i=0;i<=num;i++)
if(p[i])
ans=mod_pow(1ll*prime[i],1ll*p[i],mod)*ans%mod;
return ans;
}
int main()
{
get_prime();
int t,n,m;
scanf("%d",&t);
while(t--)
{
num=0;
memset(p,0,sizeof(p));
scanf("%d%d",&n,&m);
divide(n+1-m);
Divide(n+m,1);
Divide(n+1,0);
Divide(m,0);
ll ans=get_ans();
printf("%lld\n",ans);
}
return 0;
}