杭电3037(组合 + 逆元 + 快速幂 + 卢卡斯)
http://acm.hdu.edu.cn/showproblem.php?pid=3037
#include <iostream> #include <cstring> #include <cstdio> #include <algorithm> using namespace std; const int Max = 100100; __int64 aa[Max], p; void fn() //枚举 n! { aa[0] = 1; for(int i = 1; i <= p; i++) aa[i] = aa[i - 1] * i % p; } __int64 Pow(__int64 a, __int64 b) //快速幂计算a^b { __int64 sum = 1; while(b > 0) { if(b & 1) sum = sum * a % p; a = a * a % p; b >>= 1; } return sum; } __int64 _lukasi(__int64 a, __int64 b) //lukasi定理 { __int64 res = 1; while(a && b) { __int64 x = a % p, y = b % p; if(x < y) return 0; res = res * aa[x] * Pow(aa[y] * aa[x - y] % p, p - 2) % p; a /= p; b /= p; } return res; } int main() { int t; __int64 n, m; scanf("%d", &t); while(t--) { scanf("%I64d%I64d%I64d", &n, &m, &p); fn(); printf("%I64d\n", _lukasi(n + m, m)); } return 0; }
卢卡斯定理:
设a[x] 为x的阶乘x!
res = a[a % p] * (a[b % p] * a[(a - b) % p], p -2) % p; //1-1
lukasi(a, b, p) = lukasi(a / p, b / p, p) * res; //1-2
当a, b <100000 时,用逆元就可以了,当a, b 很大时, 就要用到lukasi定理了
c(m, n) = n! / ( (n - m)! * m!)
x ^ (p - 2) % p = (x % p) ^ (p - 2) % p //这一步很重要,就是下面 证明的前提
n!单独提出来,每P一个阶段,每个数取模P后, n! = p ^ (n / p) * (n % p) !
同理 m! = p ^ (m / p) * (m % p) !
(n - m)! = p ^ ((n - m) / p) * ((n - m) % p) !
n / p - m / p - (n - m) / p = 0