淺談盧卡斯定理
前幾天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了。
首先你需要這個算式:
其中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 }
——「看來『那個』不是什麽溫柔的謊言。」
——「似乎是這樣。」霧子微微笑著說,「最後能知道這件事實在太好了。」
淺談盧卡斯定理