可能是太太太久没有用这个定理了,考场上都不记得有这个东西,然后自己推了个组合数取模(模数是一个质数p,且p比较小),考后发现就是Lucas定理,可以说是顺推出了Lucas定理,而不是有了结论再去证明。
目标:
求 C n m ( m o d p ) C_{n}^m(mod~p) Cnm(mod p),p是质数,且比较小。
考虑如何求 n ! ( m o d p ) n!(mod~p) n!(mod p)?
当然是把p的倍数先提出来,然后剩下的数 m o d p mod~p mod p之后就是 1 , 2 , … , p − 1 1,2,…,p-1 1,2,…,p−1的周期。
∴ n ! ( m o d p ) n!(mod~p) n!(mod p)
= ( p − 1 ) ! n / p ∗ ( n m o d p ) ! ∗ p n / p ∗ ( n / p ) ! =(p-1)!^{n/p}*(n~mod~p)!*p^{n/p}*(n/p)! =(p−1)!n/p∗(n mod p)!∗pn/p∗(n/p)!
C n m ( m o d p ) C_{n}^m(mod~p) Cnm(mod p)
n ! m ! ∗ ( n − m ) ! {n!\over m!*(n-m)!} m!∗(n−m)!n!
先考虑它含 p p p的幂次为0的条件是什么?
就是 ( n / p − m / p − ( n − m ) / p ) = 0 (n/p-m/p-(n-m)/p)=0 (n/p−m/p−(n−m)/p)=0
那么其实就是 n m o d p > = m m o d p n~mod~p>=m~mod~p n mod p>=m mod p
因为 n / p = m / p + ( n − m ) / p n/p=m/p+(n-m)/p n/p=m/p+(n−m)/p,所以刚才 n ! n! n!中的 ( p − 1 ) ! n / p (p-1)!^{n/p} (p−1)!n/p这一项就被消掉了。
再代入得:
= [ n m o d p > = m m o d p ] ∗ ( n m o d p ) ! ( m m o d p ) ! ∗ ( ( n − m ) m o d p ) ! ∗ ( n / p ) ! ( m / p ) ! ∗ ( ( n − m ) / p ) ! = [n~mod~p>=m~mod~p]* {(n~mod~p)!\over(m~mod~p)!*((n-m)~mod~p)!} *{(n/p)!\over(m/p)!*((n-m)/p)!} =[n mod p>=m mod p]∗(m mod p)!∗((n−m) mod p)!(n mod p)!∗(m/p)!∗((n−m)/p)!(n/p)!
[ n m o d p > = m m o d p ] ∗ ( n m o d p ) ! ( m m o d p ) ! ∗ ( ( n − m ) m o d p ) ! [n~mod~p>=m~mod~p]* {(n~mod~p)!\over(m~mod~p)!*((n-m)~mod~p)!} [n mod p>=m mod p]∗(m mod p)!∗((n−m) mod p)!(n mod p)!⇔ C n m o d p m m o d p C_{n~mod~p}^{m~mod~p} Cn mod pm mod p
[ n m o d p > = m m o d p ] ∗ ( n / p ) ! ( m / p ) ! ∗ ( ( n − m ) / p ) ! ⇔ C n / p m / p [n~mod~p>=m~mod~p]*{(n/p)!\over(m/p)!*((n-m)/p)!}⇔C_{n/p}^{m/p} [n mod p>=m mod p]∗(m/p)!∗((n−m)/p)!(n/p)!⇔Cn/pm/p
整理得:
C n m = C n m o d p m m o d p ∗ C n / p m / p C_{n}^{m}=C_{n~mod~p}^{m~mod~p}*C_{n/p}^{m/p} Cnm=Cn mod pm mod p∗Cn/pm/p
有了上面的思路扩展Lucas定理就是手到擒来的。
我想扩展Lucas并不用给出Lucas定理那样优美的式子,只要能看就行了。
首先对模数分解质因数,之后(ex)中国剩余定理合并回去就行了。
现在问题在于求 C n m m o d p k C_{n}^{m}~mod~p^k Cnm mod pk, p p p是一个质数。
n ! m o d p k n!~mod~p^k n! mod pk
= ( ∏ i = 1 且 i m o d p > 0 p k ∗ i ) n / p k ∗ ( ∏ i = 1 且 i m o d p > 0 n m o d p k ∗ i ) ∗ ( n / p ) ! ∗ p n / p =(\prod_{i=1且i~mod~p>0}^{p^k}*i)^{n/{p^k}}*(\prod_{i=1且i~mod~p>0}^{n~mod~p^k}*i)*(n/p)!*p^{n/p} =(∏i=1且i mod p>0pk∗i)n/pk∗(∏i=1且i mod p>0n mod pk∗i)∗(n/p)!∗pn/p
( n / p ) ! (n/p)! (n/p)!可能产生新的 p p p的倍数,这个可以递归解决。
我们可以求出这样一个形式,即 n ! = p x ∗ y ( y ≠ 0 ) n!=p^{x}*y(y≠0) n!=px∗y(y̸=0),然后:
C n m = n ! m ! ∗ ( n − m ) ! C_{n}^m={n! \over m!*(n-m)!} Cnm=m!∗(n−m)!n!
这样 p p p的幂次用上面-下面算,然后y有逆元的,逆元的话可以用欧拉定理,如果写的是excrt,也可以直接用,这样就做完了。
洛谷模板题:
https://www.luogu.org/problemnew/show/P4720
Code:
#include
#define pp printf
#define ll long long
#define fo(i, x, y) for(ll i = x, B = y; i <= B; i ++)
using namespace std;
ll n, m, p;
ll mo;
ll gcd(ll x, ll y) {
return !y ? x : gcd(y, x % y);
}
void eg(ll a, ll b, ll &x, ll &y) {
if(!b) {x = a; y = 0; return;}
eg(b, a % b, y, x); y -= a / b * x;
}
ll inv(ll u, ll v) {
ll x, y; eg(u, -v, x, y);
return (x % v + v) % v;;
}
ll ksm(ll x, ll y) {
ll s = 1;
for(; y; y /= 2, x = x * x % mo)
if(y & 1) s = s * x % mo;
return s;
}
struct crt {
ll c, m;
crt(ll _c = 0, ll _m = 0) {m = _m, c = _c;}
};
crt excrt(crt a, crt b) {
ll t = gcd(a.m, b.m);
ll m3 = b.m / t, M = m3 * a.m;
crt c;
c.c = ((ll) inv(a.m / t, m3) * ((b.c - a.c) / t) % m3 * a.m + a.c) % M;
c.c < 0 ? c.c += M : 0;
c.m = M; return c;
}
ll fc1(ll n, ll p) {
return !n ? 0 : n / p + fc1(n / p, p);
}
ll fc2(ll n, ll p) {
if(!n) return 1;
ll s = 1;
fo(i, 1, mo) if(i % p) s = s * i % mo;
s = ksm(s, n / mo);
fo(i, 1, n % mo) if(i % p) s = s * i % mo;
return s * fc2(n / p, p) % mo;
}
ll C(ll n, ll m, ll p, ll k) {
mo = 1; fo(i, 1, k) mo = mo * p;
ll x = fc1(n, p) - fc1(m, p) - fc1(n - m, p);
if(x >= k) return 0;
ll y = fc2(n, p) * inv(fc2(m, p) * fc2(n - m, p) % mo, mo) % mo;
return ksm(p, x) * y % mo;
}
crt a, b;
int main() {
scanf("%lld %lld %lld", &n, &m, &p);
a = crt(0, 1);
fo(i, 2, p) if(p % i == 0) {
ll u = i, v = 0, g = 1;
while(p % i == 0) v ++, p /= i, g *= i;
b = crt(C(n, m, u, v), g);
a = excrt(a, b);
}
pp("%lld\n", a.c);
}