lucas定理 模板 學習筆記

lucas定理

(nm)modp=(npmp)(nmodpmmodp)modp=(n/pm/p)(n%pm%p)modp\tbinom{n}{m} \bmod p = \tbinom{\lfloor \frac{n}{p} \rfloor}{\lfloor \frac{m}{p} \rfloor} \tbinom{n \bmod p}{m \bmod p} \bmod p=\tbinom{n/p}{m/p}\tbinom{n\%p}{m\%p} \bmod p

先預先求出i!  (i[0,p))i! \;(i \in \left[0,p\right)).
並利用費馬小定理和快速冪乘求出每一個i!i!的逆元(i!)1(i!)^{-1}。求(nm)modp\tbinom{n}{m} \bmod p,當m=0m=0直接就是11.若n,mn,m都在pp範圍內,則直接轉化爲n!×(m!)1×[(nm)!]1n! \times (m!)^{-1} \times [(n-m)!]^{-1}.否則就是lucas定理縮小規模。

[對一個固定的p,預處理求階乘及快速模冪求其逆元,時間複雜度O(plog2p)O(p\log_2{p})。空間複雜度O(p)O(p)。預處理之後,單次求(nm)modp\tbinom{n}{m} \bmod p複雜度O(logpm)O(\log_{p}{m})]{}

洛谷P3807模板題

void prepare(ll p, vector<ll>&fac, vector<ll>&inv_fac) {
    fac.resize(p); inv_fac.resize(p);
    mod_sys mod;
    mod.set_mod(p);
    fac[0] = 1;
    inv_fac[0] = 1;
    for (int i = 1; i < p; ++i) {
        fac[i] = (fac[i-1]*i)%p;
        inv_fac[i] = mod.pow(fac[i], p-2); // 既然能枚舉一遍,p*p不應該爆ll
    }
}

// 輸入預設0=<n,m<p
inline ll combination(ll n, ll m, ll p, vector<ll>&fac, vector<ll>&inv_fac) {
    if (n < m) return 0;
    return fac[n]*inv_fac[m]%p*inv_fac[n-m]%p;
}

ll lucas(ll n, ll m, ll p, vector<ll>&fac, vector<ll>&inv_fac) {
    if (n < m) return 0;
    ll ans = 1;
    while(true) {
        if (m == 0) return ans;
        if (n < p && m < p) return ans*combination(n,m,p,fac,inv_fac)%p;
        ans = ans * combination(n%p,m%p,p,fac,inv_fac)%p;
        n/=p; m/=p;
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章