浅谈卢卡斯定理
前几天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 }
——「看来『那个』不是什么温柔的谎言。」
——「似乎是这样。」雾子微微笑着说,「最后能知道这件事实在太好了。」