Lucas定理与扩展

٩ ( > < ) ۶ L u c a s

( n m ) mod p p 为质数。 1 n , m , p 10 5

(1) n = n k p k + n k 1 p k 1 + + n 1 p + n 0 (2) m = m k p k + m k 1 p k 1 + + m 1 p + m 0

( n m ) i = 0 k ( n i m i ) ( mod p )

也就是

( n m ) ( n p m p ) ( n mod p m mod p ) ( mod p )

考虑

( p i ) = p ! i ! ( p i ) !   ( 0 < i < p )

由于 i ! ( p i ) ! p ,因此

( p i ) 0 ( mod p )   ( 0 < i < p )

s p + q = n t p + r = m ,有

( x + 1 ) s p + q = [ ( x + 1 ) p ] s ( x + 1 ) q

由二项式定理得

( x + 1 ) p = i = 0 p ( p i ) x i

( p i ) 0 ( mod p )   ( 0 < i < p ) ,因此

( x + 1 ) p x p + 1 ( mod p )

所以

( x + 1 ) s p + q ( x p + 1 ) s ( x + 1 ) q ( mod p )

由二项式定理得

(3) ( x + 1 ) s p + q = i = 0 s p + q ( s p + q i ) x i   (4) ( x p + 1 ) s ( x + 1 ) q = i = 0 s ( s i ) x p i j = 0 q ( q j ) x j  

由于 x t p + r 在 ① 式中的系数为 ( s p + q t p + r ) ,在 ② 式中的系数为 ( s t ) ( q r ) ,因此

( s p + q t p + r ) = ( s t ) ( q r ) ( mod p )

( n m ) ( n p m p ) ( n mod p m mod p ) ( mod p )


٩ ( > < ) ۶ L u c a s

( n m ) mod p ,不保证 p 为质数。 1 m n 10 18 , 2 p 1000000

p = p 1 k 1 p 2 k 2 p r k r ,有

{ x c 1 ( mod p 1 k 1 ) x c 2 ( mod p 2 k 2 ) x c r ( mod p r k r )

考虑如何计算 n ! mod p k ,以 n = 19 , p = 3 , k = 2 为例,有

(5) n ! = 1 × 2 × × 19 (6) = ( 1 × 2 × 4 × 5 × × 16 × 17 × 19 ) p × ( 3 × 6 × × 18 ) p (7) = ( 1 × 2 × 4 × 5 × × 16 × 17 × 19 ) × 3 6 ( 1 × 2 × × 6 )

1 × 2 × 4 × 5 × 7 × 8 10 × 11 × 13 × 14 × 16 × 17 ( mod p k ) ,因此

n ! = ( 1 × 2 × 4 × 5 × 7 × 8 ) 2 × 19 × 3 6 × 6 ! ( mod p k )

f ( n ) 表示 n ! 中有多少个因数 p ,有递推式

f ( n ) = f ( n p ) + n p

f ( 19 ) = 8   ( 3 , 6 , 9 , 12 , 15 , 18 )   ( 3 , 2 3 , 3 3 , 4 3 , 5 3 , 2 3 3 ) ,其中 9 18 f ( 6 ) 中的 3 6 3 转移得到。

递归求解 ( n m ) mod p i k i ,最后中国剩余定理合并即可。

总复杂度 O ( n l o g n )

至于 e x c r t 的数组究竟开多大,你会发现将 10 9 以内的模数分解成 p 1 k 1 p 2 k 2 p r k r 的话, r 最大是 9

【模板】扩展卢卡斯

// luogu-judger-enable-o2
#include <cstdio>
#include <cmath>

typedef long long LL;
const LL N = 1000005;
LL c[N], mo[N], cnt;

LL pow(LL a, LL b, LL p) {
    LL res = 1;
    for (; b; b >>= 1, a = a * a % p)
        if (b & 1) res = res * a % p;
    return res;
}
LL exgcd(LL a, LL b, LL &x, LL &y) {
    if (b == 0) { x = 1, y = 0; return a; }
    LL t = exgcd(b, a % b, y, x); y -= a / b * x; return t;
}
LL inv(LL a, LL p) {
    LL x, y, t = exgcd(a, p, x, y);
    return (x % p + p) % p;
}
LL fac(LL n, LL pi, LL pk) {
    if (n == 0) return 1;
    LL res = 1;
    for (LL i = 2; i <= pk; ++i)
        if (i % pi) res = res * i % pk; //1*2*4*5*7*8
    res = pow(res, n / pk, pk); //(1*2*4*5*7*8)^2
    for (LL i = 2; i <= n % pk; ++i)
        if (i % pi) res = res * i % pk; //19
    return (res * fac(n / pi, pi, pk)) % pk; //6!
}
void calc(LL n, LL m, LL pi, LL pk) {
    LL up = fac(n, pi, pk), d1 = fac(n - m, pi, pk), d2 = fac(m, pi, pk);
    LL k = 0; //3^6
    for (LL i = n; i; i /= pi) k += i / pi;
    for (LL i = m; i; i /= pi) k -= i / pi;
    for (LL i = n - m; i; i /= pi) k -= i / pi;
    c[++cnt] = pow(pi, k, pk) % pk * up % pk * inv(d1, pk) % pk * inv(d2, pk) % pk;
    mo[cnt] = pk;
}
LL mul(LL a, LL b, LL p) {
    LL res = 0, f = 1;
    if (a < 0) f = -f, a = -a;
    if (b < 0) f = -f, b = -b;
    for (; b; b >>= 1, a = (a + a) % p)
        if (b & 1) res = (res + a) % p;
    return res * f;
}
LL excrt() {
    LL x, y, t, mod;
    for (LL i = 2; i <= cnt; ++i) {
        t = exgcd(mo[1], mo[i], x, y);
        if ((c[i] - c[1]) % t) return -1;
        mod = mo[1] / t * mo[i];
        x = mul(x, (c[i] - c[1]) / t, mod);
        c[1] = (c[1] + mul(x, mo[1], mod)) % mod;
        mo[1] = mod;
    }
    return (c[1] + mo[1]) % mo[1];
}
LL exlucas(LL n, LL m, LL p) {
    LL lim = sqrt(p), tmp = p, pk;
    for (LL i = 2; i <= lim; ++i) {
        if (tmp % i == 0) {
            pk = 1;
            while (tmp % i == 0) pk *= i, tmp /= i;
            calc(n, m, i, pk);
        }
    }
    if (tmp > 1) calc(n, m, tmp, tmp);
    return excrt() % p;
}

int main() {
    LL n, m, p;
    scanf("%lld%lld%lld", &n, &m, &p);
    printf("%lld", exlucas(n, m, p));
    return 0;
}

猜你喜欢

转载自blog.csdn.net/Milkyyyyy/article/details/82083191