Description
Compute M choose N mod 10007.
Input
The first line of input is the number of test case.
The only one line of each test case contains two integers,
M and N (0 <= N <= M <= 200,000,000).
There is a blank line before each test case.
Output
For each test case output the answer on a single line:
C(M,N)%10007.
Sample Input
4
5 1
5 2
0 0
70074 50048
Sample Output
5
10
1
9916
解法:
因为N和M的范围是(0 <= N <= M <= 200,000,000),所以用分解阶乘的方法是行不通的,要解这个题,必须要用到lacus定理:
现在需要解决的问题是求组合数C(m,n)%p其中的m和n都在10007的范围内,这个问题可以用同余模方程解决:n!/(m!*(n-m)! =x%p ,先对算出n!、m!、(n-m)!对p取模的余数,就转换为a/b=x%p;因为p为素数,所以等价于bx+py=a;然后用扩展的欧几里得定理算出bx'+py'=1的解,x=x'*a,就得到了最终的x的值,即C(m,n)%p得值。
代码:
#include<cstdio>
#include<memory>
using namespace std;
const int mod=10007;
int a[mod];
void init()
{
int i;
a[0]=1;
for(i=1;i<mod;i++)
a[i]=(a[i-1]*i)%mod;
}
int gcd(int a,int b)
{
if(b==0)
return a;
return gcd(b,a%b);
}
void e_gcd(int a,int b,int &x,int &y) //扩展欧几里得定理:解ax+by==1。
{
if(!b)
{
x=1;
y=0;
}
else
{
e_gcd(b,a%b,x,y);
int l=x;
x=y;
y=l-a/b*y;
}
}
int choose(int n,int m)
{
if(m>n)
return 0;
else if(n==m)
return 1;
int nn=a[n],mm=(a[m]*a[n-m])%mod;
int d=gcd(nn,mm);
nn/=d;
mm/=d;
int x,y;
e_gcd(mm,mod,x,y);
x=(x+mod)%mod;
return (x*nn)%mod;
}
int main( )
{
int t;
scanf("%d",&t);
init();
while(t--)
{
int e[100],f[100];
int i=0,j,m,n;
memset(e,0,sizeof(e));
memset(f,0,sizeof(f));
scanf("%d %d",&n,&m);
while(n>0)
{
e[i++]=n%mod;
n=n/mod;
}
int len=i;
i=0;
while(m>0)
{
f[i++]=m%mod;
m=m/mod;
}
int re=1;
for(i=0;i<len;i++)
{
re=(re*choose(e[i],f[i]))%mod;
}
printf("%d/n",re%mod);
}
return 0;
}