组合数之Lucas定理

逆元

  (A/B)%p问题

(A/B)%p==A*inv(B,p)%p;

证明:逆元t满足(B*t)%p=1。

那么 (A/B)%p=(A/B)*(B*t)%p=A*t%p;(这里A%B==0)

接下来求逆元:

gcd(a,b)=a*x+b*y=d;  ==> a*x=d%b;

当d==1时:   x为a%b的逆元,   y为b%a的逆元。  一般模数都是素数。

组合数C(n,m)%p=n*(n-1)*(n-m+1)/m! %p   属于(a/b)%p模型。

那么可以利用这个模型来求解组合数。  c(n,m)=c(n,m-1)*(n-m+1)/m;

 

 1 LL CM(LL n,LL m,LL p)
 2 {
 3     LL a=1,b=1;
 4     if(m>n)
 5         return 0;
 6     //c(n,m)=c(n,m-1)*(n-m+1)/m;  O(m) 求c(n,m)
 7     while(m)
 8     {
 9         a=(a*n)%p;
10         b=(b*m)%p;
11         m--;
12         n--;
13     }
14     return (a*inv(b,p))%p;
15 }

 

Lucas定理

A、B是非负整数,p是质数。AB写成p进制:A=a[n]a[n-1]...a[0],B=b[n]b[n-1]...b[0]。

组合数C(A,B)与C(a[n],b[n])*C(a[n-1],b[n-1])*...*C(a[0],b[0])  modp 相等

即:Lucas(n,m,p)=c(n%p,m%p)*Lucas(n/p,m/p,p) 

//   Lucas(n,m,p)=c(n%p,m%p)*Lucas(n/p,m/p,p)   递归求解
LL Lucas(LL n,LL m,LL p)
{
    if(m==0)
        return 1;
    return (CM(n%p,m%p,p)*Lucas(n/p,m/p,p))%p;
}

 

 

 1 #include <iostream>
 2 #include <cstdio>
 3 #include <cstring>
 4 #include <cmath>
 5 #include <algorithm>
 6 using namespace std;
 7 typedef long long LL;
 8 
 9 void ex_gcd(LL a,LL b,LL &d,LL &x,LL &y)
10 {
11     if(b==0)
12     {
13         x=1;y=0;d=a;
14     }
15     else
16     {
17         ex_gcd(b,a%b,d,y,x);
18         y-=(a/b)*x;
19     }
20 }
21 
22 LL inv(LL a,LL m)
23 {
24     LL d,x,y;
25     ex_gcd(a,m,d,x,y);
26     if(d==1)
27         return (x%m+m)%m;    //x可能为负数
28     return -1;
29 }
30 
31 LL CM(LL n,LL m,LL p)
32 {
33     LL a=1,b=1;
34     if(m>n)
35         return 0;
36     //c(n,m)=c(n,m-1)*(n-m+1)/m;  O(m) 求c(n,m)
37     while(m)
38     {
39         a=(a*n)%p;
40         b=(b*m)%p;
41         m--;
42         n--;
43     }
44     return (a*inv(b,p))%p;
45 }
46 
47 //   Lucas(n,m,p)=c(n%p,m%p)*Lucas(n/p,m/p,p)
48 LL Lucas(LL n,LL m,LL p)
49 {
50     if(m==0)
51         return 1;
52     return (CM(n%p,m%p,p)*Lucas(n/p,m/p,p))%p;
53 }
54 
55 int main()
56 {
57 
58     int T;
59     scanf("%d",&T);
60     while(T--)
61     {
62         LL n,m,p;
63         scanf("%lld%lld%lld",&n,&m,&p);
64         printf("%lld\n",Lucas(n+m,m,p));
65     }
66     return 0;
67 }


Lucas定理中包含了两种求解组合数得算法。

一般:

对于大组合数取模,n,m不大于10^5的话,用逆元的方法,可以解决。

对于n,m大于10^5的话,那么要求p<=10^5,这样就是Lucas定理了,将n,m转化到10^5以内解。

 

 

  

posted on 2015-05-09 11:55  hutaishi  阅读(182)  评论(0)    收藏  举报

导航