淺談盧卡斯定理

前幾天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 }

 

               ——「看來『那個』不是什麼溫柔的謊話。」               ——「彷佛是這樣。」霧子微微笑着說,「最後能知道這件事實在太好了。」

相關文章
相關標籤/搜索