乘法逆元&lucas定理

乘法逆元&lucas定理

乘法逆元

乘法逆元性质

若整数bbmm互质,并且对于任意的整数 aa,如果满足 bab|a,则存在一个整数 xx,使得 a/ba×x(modm)a/b≡a×x(mod m),则称 xxbb 的模 mm 乘法逆元,记为b1(modm)b^{-1}(mod m)bb 存在乘法逆元的充要条件是 bb 与模数 mm 互质。当模数 mm 为质数时,bm2b^{m−2} 即为 bb 的乘法逆元。

乘法逆元证明

inv(x)inv(x)为b的逆元且n为质数

a/bainv(b)(modn)a/b ≡ a*inv(b)(mod n)

a ≡ a * b * inv(b) (mod n)$

b*inv(b) ≡ 1 (mod n)$

由费马小定理可得bn11(modn)b ^ {n - 1} ≡ 1 (mod n)

所以bbn21(modn)b * b ^ {n - 2} ≡ 1 (mod n)

故n为质数时,inv(b)=bn2inv(b) = b^{n - 2}

很显然如果aann的倍数则逆元不存在

代码

#include <iostream>
using namespace std;
#define ll long long
ll t;
ll qim(ll a, ll b, ll m){
    ll res = 1;
    while(b){
        if(b & 1){
            res *= a;
            res %= m;
        }
        a *= a;
        a %= m;
        b >>= 1;
    }
    return res;
}
int main(){
    cin >> t;
    while(t --){
        ll a, b; cin >> a >> b;
        ll res = qim(a, b - 2, b);
        if(a % b) cout << res << endl;
        else cout << "impossible" << endl;
    }
    return 0;
}

lucas定理

lucas定理的作用

lucas定理是一个与组合数有关的数论定理,根据组合定义Cmn=m!/n!(mn)!=m(m1)(mn+1)/n!C^n_m = m! / {n!(m - n)!} = {m * (m - 1) * ··· * (m - n + 1)} / n!如果当m>30m > 30很明显数值无法用longlonglonglong承担,不过我们可以通过乘法逆元将除法转换为乘法Cmnm!inv(n!)inv[(mn)!](modP)C^n_m ≡ m! * inv(n!) * inv[(m - n)!] (mod P),虽然可以通过这种方式解决问题,但有时候质数P<m!P<m!,这样就无法保证n!n!mn!m-n!的逆元是否存在了,因为有可能是PP的倍数,这时候就要使用lucas定理

lucas定理

对于非负整数 n,mn,m和质数 PPCmni=0kCmini(modP)C^n_m ≡ \prod_{i = 0}^k{C^{n_i}_{m_i}} (mod P) ,其中m=mkpk++m1p+m0,n=nkpk++n1p+n0m = m_kp^k + ··· +m_1p + m_0, n = n_kp^k+···+n_1p+n_0mmnnpp进制展开,式子也可得Cmn=Cm%pn%pCm/pn/p(modp)C^n_m=C^{n\%p}_{m\%p}⋅C^{⌊n/p⌋}_{⌊m/p⌋}(modp)

lucas定理证明

前置知识:

二项式公式(a+b)n=Cn0bn+Cn1a1bn1++Cnnan=k=0nCnkakbnk(a+b)^n=C^0_nb^n+C^1_na^1⋅b^{n−1}+⋯+C_n^na^n=\sum_{k = 0}^n{C^k_na^kb^{n-k}}

式子一:Cpx0(modp),0<x<pC^x_p≡0(modp), 0<x<p 其中pp为质数

由于 0<x<p0<x<p,并且 pp 是质数,所以Cpx=p!/x!(px)!=p(p1)!/x(x1)!(px)!=p/xCp1x1C^x_p=p!/{x!*(p−x)!}=p*(p−1)!/{x*(x−1)!*(p−x)!}=p/x*C^{x−1}_{p−1}

因为 x<px < p,所以p/xp/x存在xxpp意义下的逆元inv(x)inv(x)

Cpxpinv(x)Cp1x1(modp)C^x_p≡p*inv(x)*C^{x-1}_{p-1} (mod p)

显然右边为膜数pp的倍数,所以Cpx0(modp)C^x_p≡0 (mod p)

式子二:(1+x)p1+xp(modp)(1+x)^p≡1+x^p(modp)

由二项式展开可得:(1+x)p=Cp0xp+Cp1xp1++Cpp=k=0pCpkxk(1+x)^p=C^0_px^p+C^1_p⋅x^{p−1}+⋯+C_p^p=\sum_{k = 0}^p{C^k_px^{k}}

将式子一代入可得:(1+x)p1+xp(modp)(1+x)^p ≡ 1 + x^p (mod p)

lucas定理证明: Cmn=Cm%pn%pCm/pn/p(modp)C^n_m=C^{n\%p}_{m\%p}⋅C^{⌊n/p⌋}_{⌊m/p⌋}(modp)

{m/p=qmn/p=qn,{m%p=rmn%p=rn,{m=qmp+rmn=qnp+rn\{^{⌊n/p⌋ = q_n}_{⌊m/p⌋ = q_m},\{_{m\%p = r_m}^{n\%p = r_n},\{_{m=q_mp+r_m}^{n=q_np+r_n}

再由二项式定理(1+x)m=k=0mCmkxk(1+x)^m=\sum_{k = 0}^m{C^k_mx^{k}}

可得(1+x)m=(1+x)qmp+rm=(1+x)qmp(1+x)rm=[(1+x)p]qm(1+x)rm(1+xp)qm(1+x)rm=i=0qmCqmixpij=0rmCrmjxj=i=0qmj=0rmCqmiCrmjxpi+j(modp)(1+x)^m=(1+x)^{q_mp+r_m}=(1+x)^{q_mp}*(1+x)^{r_m} = [(1+x)^p]^{q_m}*(1+x)^{r_m}≡(1+x^p)^{q_m}*(1+x)^{r_m}=\sum_{i = 0}^{q_m}{C^i_{q_m}x^{pi}}*\sum_{j = 0}^{r_m}{C^j_{r_m}x^{j}}=\sum^{q_m}_{i = 0}\sum^{r_m}_{j=0}{C^i_{q_m}C^j_{r_m}x^{pi+j}} (mod p)

之后归纳总结出xip+jx^{ip+j}的系数k=ip+jk = ip+j

得:(1+x)mi=0mCqmi/pCrmn%pxi(modp)(1+x)^m≡\sum^{m}_{i = 0}{C^{⌊i/p⌋}_{q_m}C^{⌊n\%p⌋}_{r_m}x^{i}} (mod p)

k=0mCmkxki=0mCqmi/pCrmn%pxi(modp)\sum^{m}_{k = 0}{C^{k}_{m} x^k} ≡ \sum^{m}_{i = 0}{C^{⌊i/p⌋}_{q_m}C^{⌊n\%p⌋}_{r_m}x^i} (mod p)

k=0mCmki=0mCqmi/pCrmn%p(modp)\sum^{m}_{k = 0}{C^{k}_{m}} ≡ \sum^{m}_{i = 0}{C^{⌊i/p⌋}_{q_m}C^{⌊n\%p⌋}_{r_m}} (mod p)

对比系数,且k=nk=n,则CmnCrmn%pCqmn/pCmn=Cm%pn%pCm/pn/p(modp)C^n_m≡ C^{n\%p}_{r_m}⋅C^{⌊n/p⌋}_{q_m}≡ C^n_m=C^{n\%p}_{m\%p}⋅C^{⌊n/p⌋}_{⌊m/p⌋} (modp) 得证

代码

#include <iostream>
using namespace std;
#define ll long long
ll t;
ll a, b, p;
ll qim(ll a, ll b, ll p){
    ll res = 1;
    while(b){
        if(b & 1){
            res *= a;
            res %= p;
        }
        a *= a;
        a %= p;
        b >>= 1;
    }
    return res;
}
ll C(ll a, ll b){
    ll res = 1;
    for(ll i = 1, j = a; i <= b; j --, i ++){
        res = (j * res) % p;
        res = (res * qim(i, p - 2,  p)) % p;
    }
    return res;
}
ll lucas(ll a, ll b, ll p){
    if(a < p && b < p ) return C(a, b);
    return C(a % p, b % p) * lucas(a / p, b / p, p) % p;
}
int main(){
    cin >> t;
    while(t --){
        cin >> a >> b >> p;
        cout << lucas(a, b, p) << endl;
    }
    return 0;
}
赞赏