1. 程式人生 > >淺談盧卡斯定理

淺談盧卡斯定理

smi tdi 能夠 char 快速 get through for 除法取模

前幾天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 + xp) s · (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 }

               ——「看來『那個』不是什麽溫柔的謊言。」
               ——「似乎是這樣。」霧子微微笑著說,「最後能知道這件事實在太好了。」

淺談盧卡斯定理