扩展卢卡斯定理学习笔记

扩展卢卡斯定理学习笔记

【模板】扩展卢卡斯

前置知识

  1. \(exgcd\)
  2. 中国剩余定理
  3. 一定数学能力

卢卡斯定理是用来求\(C_n^m \bmod{p}\)的工具,若\(p\)为质数,则\(C_n^m\equiv C_{\lfloor{\frac{n}{p}}\rfloor}^{\lfloor{\frac{m}{p}}\rfloor}*C_{n\%p}^{m\%p}(\bmod \ p)\)

但是当\(p\)不是质数的时候,就需要用到扩展卢卡斯定理了.下面简单说一下过程:

1

将模数\(P\)分解成\(\prod p_i^{k_i}\)的形式,也就是将\(P\)表示成若干个质数的指数的乘积形式.

2

设答案为\(ans\),则有\[ans\equiv C^{m}_{n}(\mod \ p_i ^{k_i})\],也就是说,我们只需要求解\(C_n^m\)在模某个质数的几次幂下的结果,然后将这些结果用中国剩余定理合并就可以求出答案.

3

下面用\(p\)表示\(p_i\),\(k\)表示\(k_i\).
\[C_n^m=\frac{n!}{m!*(n-m)!}\]
\(n!,m!,(n-m)!\)中含有\(p_i\)的因子提出来,将上面的式子进行变形,则有:
\[C_n^m\equiv \frac{\frac{n!}{p^{a1}}}{\frac{m!}{p^{a2}}*\frac{(n-m)!}{p^{a3}}}*p^{a1-a2-a3}(\mod p^k)\]
那么此时式子的左半边都是与\(p^k\)互质的,可以直接用\(exgcd\)求逆元,右边可以快速幂解决.

4

现在我们需要解决\[\frac{n!}{p^{a1}}\],根据别人得出的式子,我们可以知道:
\[n!\equiv p^{\lfloor \frac{n}{p} \rfloor}*\lfloor \frac{n}{p} \rfloor!*(\prod_{i=1}^{p^k}num_i)^{\lfloor \frac{n}{p^k} \rfloor}*(\prod_{i=1}^{n\%p^k}num_i)(\mod \ p^k)\]
其中\(num_i\)表示不含\(p\)因子的数字.
其中\((\prod_{i=1}^{p^k}num_i)^{\lfloor \frac{n}{p^k} \rfloor}\)可以直接枚举\(1\)\(p^k\)的数字乘起来,然后次方做一遍快速幂.
\((\prod_{i=1}^{n\%p^k}num_i)\)直接枚举乘起来,\(\lfloor \frac{n}{p} \rfloor!\)递归求解,边界是当\(n=0\)时,返回\(1\).
因为我们要求\(\frac{n!}{p^{a1}}\),所以在乘的时候可以直接把式子第一部分的\(p^{\lfloor \frac{n}{p} \rfloor}\)直接忽略掉.

int fac(int n, int pi, int pk){
    if(!n) return 1;
    int res = 1;
    for(int i = 2; i < pk; i++)
        if(i % pi) (res *= i) %= pk;
    res = qpow(res, n/pk, pk);
    for(int i = 2; i <= n%pk; i++)
        if(i % pi) (res *= i) %= pk;
    return fac(n/pi, pi, pk)*res%pk;
}

5

根据\[C_n^m\equiv \frac{\frac{n!}{p^{a1}}}{\frac{m!}{p^{a2}}*\frac{(n-m)!}{p^{a3}}}*p^{a1-a2-a3}(\mod p^k)\],计算出了左边式子的上下部分,就可以直接用\(exgcd\)求出下面两个结果在模\(p^k\)意义下的逆元,然后乘起来.

inline int C(int n, int m, int pi, int pk){
    if(n < m) return 0;
    int r1 = fac(n, pi, pk), r2 = fac(m, pi, pk);
    int r3 = fac(n-m, pi, pk), cnt = 0;
    for(int i = n; i; i /= pi) cnt += i/pi;
    for(int i = m; i; i /= pi) cnt -= i/pi;
    for(int i = n-m; i; i /= pi) cnt -= i/pi;
    return r1*inv(r2, pk)%pk*inv(r3, pk)%pk*qpow(pi, cnt, pk)%pk;
}

6

再根据\[ans\equiv C^{m}_{n}(\mod \ p ^{k})\],我们可以用中国剩余定理合并这些结果(这里我用的是扩展中国剩余定理)


inline int exlucas(int n, int m, int p){
    int pi, pk, res = 0, x, y, gcd, c, M = 1;
    for(int i = 2; i*i <= p; i++){
        if(p % i == 0){
            pi = i, pk = 1;
            while(p % i == 0) p /= i, pk *= i;
            gcd = exgcd(M, pk, x, y), c = Mod((C(n, m, pi, pk)-res)%pk+pk, pk);
            x = Mod(x*(c/gcd)%pk+pk, pk);
            res += M*x, M *= pk/gcd, res %= M;
        }
    }
    if(p > 1){ // 最后分解质因数后可能存在一个大于sqrt(p)的大质数
        pi = pk = p; gcd = exgcd(M, pk, x, y);
        c = Mod((C(n, m, pi, pk)-res)%pk+pk, pk);
        x = Mod(x*(c/gcd)%pk+pk, pk);
        res += M*x, M *= pk/gcd, res %= M;
    }
    return res;
}

下面放一下完整代码

#include<bits/stdc++.h>
using namespace std;
typedef int _int;
#define int long long

int n, m, p;

int exgcd(int a, int b, int &x, int &y){
    if(!b){ x = 1, y = 0; return a; }
    int gcd = exgcd(b, a%b, x, y), tmp;
    tmp = x, x = y, y = tmp-a/b*y;
    return gcd;
}

inline int Mod(int x, int mod){ return x > mod ? x-mod : x; }

inline int inv(int a, int mod){
    int x, y; exgcd(a, mod, x, y);
    return (x%mod+mod)%mod;
}

inline int qpow(int x, int n, int mod){
    int res = 1;
    for(; n; x = x*x%mod, n >>= 1)
        if(n & 1) (res *= x) %= mod;
    return res;
}

int fac(int n, int pi, int pk){
    if(!n) return 1;
    int res = 1;
    for(int i = 2; i < pk; i++)
        if(i % pi) (res *= i) %= pk;
    res = qpow(res, n/pk, pk);
    for(int i = 2; i <= n%pk; i++)
        if(i % pi) (res *= i) %= pk;
    return fac(n/pi, pi, pk)*res%pk;
}

inline int C(int n, int m, int pi, int pk){
    if(n < m) return 0;
    int r1 = fac(n, pi, pk), r2 = fac(m, pi, pk);
    int r3 = fac(n-m, pi, pk), cnt = 0;
    for(int i = n; i; i /= pi) cnt += i/pi;
    for(int i = m; i; i /= pi) cnt -= i/pi;
    for(int i = n-m; i; i /= pi) cnt -= i/pi;
    return r1*inv(r2, pk)%pk*inv(r3, pk)%pk*qpow(pi, cnt, pk)%pk;
}

inline int exlucas(int n, int m, int p){
    int pi, pk, res = 0, x, y, gcd, c, M = 1;
    for(int i = 2; i*i <= p; i++){
        if(p % i == 0){
            pi = i, pk = 1;
            while(p % i == 0) p /= i, pk *= i;
            gcd = exgcd(M, pk, x, y), c = Mod((C(n, m, pi, pk)-res)%pk+pk, pk);
            x = Mod(x*(c/gcd)%pk+pk, pk);
            res += M*x, M *= pk/gcd, res %= M;
        }
    }
    if(p > 1){
        pi = pk = p; gcd = exgcd(M, pk, x, y);
        c = Mod((C(n, m, pi, pk)-res)%pk+pk, pk);
        x = Mod(x*(c/gcd)%pk+pk, pk);
        res += M*x, M *= pk/gcd, res %= M;
    }
    return res;
}

_int main(){
    cin >> n >> m >> p;
    cout << exlucas(n, m, p) << endl;
    return 0;
}

转载于:https://www.cnblogs.com/BCOI/p/10368553.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值