浅谈卢卡斯定理

前几天gryz组织我们听了几天数论,蒟蒻 Nanjo_Qi 自然是听得一点问题也没有。

于是只能自己yy着学一点其他的数学的东西,正巧在那之前刚刚学会卢卡斯定理,于是现在就来水一篇博客。

其实是不想做题了。正巧机房装修,吵的一批。

卢卡斯(Lucas)定理是什么?

他是用来求组合数 C(n, m) % p 值的定理,这里的p是素数。所以,它是一个解决大组合数求模的算法。所以看起来还是很有用的感觉。

 

为了给以后的学习做铺垫,我们先来了解一下乘法逆元:

逆元是指一个可以取消另一给定元素运算的元素,例如加法中的加法逆元和乘法中的倒数。

而一个数关于模p意义下的逆元可以利用快速幂,扩展欧几里得算法等求得:

已知(a, p) = 1,则 ap-1 ≡ 1 (mod p),  所以 a * ap-2 ≡ 1 (mod p) ,也就是 (m!(n-m)!) 的逆元为 (m!(n-m)!)p-2 

 1 typedef u64 long long;
 2 
 3 inline u64 Fast_Pow(u64 k, u64 b) {
 4     u64 ans = 1;
 5     while(b) {
 6         if( b&1 )  ans = ans * k % p;
 7         k = k * k % p, b >>= 1;
 8     }
 9     return ans;
10 }
乘法逆元

 

然后就可以愉快地学习Lucas了。

推导过程如下(来自百度百科,计算机竞赛不需要证明,所以不想看就算了(bushi)):

首先你需要这个算式:

   
 

其中f > 0 && f < p,然后

(1 + x) n Ξ (1 + x) sp+q  Ξ ( (1 + x)p)s · (1 + x) q Ξ (1 + xps · (1 + x) q   (mod p)

所以得(1 + x) sp+q    (mod p)

我们求左边 (1 + x)sp+q 中的   的系数为:

求右边公式中的    :

通过观察你会发现当且仅当i = t , j = r ,能够得到   的系数,即: 

所以   ,得证。

反正我是没仔细看懂,所以对不对我也不知道。

 

你只要知道:C(n, m) % p = (C(n/p, m/p) % p) * (C(n%p, m%p) % p) % p 就好了吧?(笑

然后程序可以对 C(n%p, m%p) % p 这个地方递归调用Lucas定理;

以及前面 C(n, m) % p = n! / ( m!(n - m)! ) % p 的除法取模,求一下 n! / ( m!(n - m)! ) 模p意义下的逆元就好了。

其实就是一个公式的东西233,很简单的板子,转眼水完了半上午

 

 1 #include <cstdio>
 2 #include <cctype>
 3 #include <cstring>
 4 #include <algorithm>
 5 using namespace std;
 6 
 7 typedef long long u64;
 8 
 9 const int maxn = 100000 + 10;
10 u64 n, m, p, fac[maxn];
11 
12 inline u64 Fast_Pow(u64 x, u64 k) {
13     u64 ans = 1;
14     while(k) {
15         if( k&1 ) ans = ans * x % p;
16         k >>= 1, x = x * x % p;
17     }
18     return ans;
19 }
20 
21 u64 C(u64 k, u64 b) {
22     if( k<b )  return 0;
23     return fac[k] * Fast_Pow(fac[b] % p, p-2) % p * Fast_Pow(fac[k-b] % p, p-2) % p;
24 }
25 
26 u64 Lucas(u64 k, u64 b) {
27     if( !b )  return 1;
28     return C(k%p, b%p) * Lucas(k/p, b/p) % p;
29 }
30 
31 int main(int argc, char const *argv[])
32 {
33     int t;  scanf("%d", &t);
34     while( t-- ) {
35         fac[0] = 1;
36         scanf("%lld%lld%lld", &n, &m, &p);
37         for(int i=1; i<=p; ++i)
38             fac[i] = fac[i-1] * i % p;
39         printf("%lld\n", Lucas(n+m, m));
40     }
41     return 0;
42 }

 

               ——「看来『那个』不是什么温柔的谎言。」
               ——「似乎是这样。」雾子微微笑着说,「最后能知道这件事实在太好了。」

 

posted @ 2018-05-26 10:17  南條雪绘  阅读(2933)  评论(4编辑  收藏  举报