lucas定理 模板

lucas定理

(nm) mod p=(npmp)(n mod pm mod p) mod p=(n/pm/p)(n%pm%p) mod p\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) mod p\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) mod p\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;
    }
}
posted @ 2019-09-05 02:16  我云知世就是力量  阅读(104)  评论(0)    收藏  举报