组合数取模

 

组合数取模

http://hi.baidu.com/scuxy06/blog/item/4b5b3f1921b29b72dab4bddb.html

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
#include
using namespace std;
const int mod=10007;
int a[mod];
void init()
{
    int i;
    a[0]=1;
    for(i=1;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      {
           re=(re*choose(e[i],f[i]))%mod;
      }
       printf("%d/n",re%mod);
   }
   return 0;
}

 
 

你可能感兴趣的:(output,each,input,扩展,c)