浅谈卢卡斯定理

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

因而只能本身yy着学一点其余的数学的东西,正巧在那以前刚刚学会卢卡斯定理,因而如今就来水一篇博客。ide

实际上是不想作题了。正巧机房装修,吵的一批。学习

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

他是用来求组合数 C(n, m) % p 值的定理,这里的p是素数。因此,它是一个解决大组合数求模的算法。因此看起来仍是颇有用的感受。code

 

为了给之后的学习作铺垫,咱们先来了解一下乘法逆元:orm

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

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

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

 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 }

 

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

相关文章
相关标签/搜索