卢卡斯定理

对于这种问题C(n, m) % p, n, m, p 都比较大的, 就要用卢卡斯定理了, 但是这里又分了两种情况, 一种是n, m 在1e6以内的, 并且case比较多的, 那么我们每次取调用卢卡斯肯定会T的, 所以我们要做的就是朴素的算就是了, 也就是直接调用组合数公式, 这个时候我们只要先预处理好1e6以内的带mod阶乘就行啦…. 类似于离线求

const int maxn = 1e5+5;
ll f[maxn];
void init() { // 预处理阶乘
    f[0] = f[1] = 1;
    for (int i = 2 ; i <= 100001 ; i ++) {
        f[i] = i % mod * f[i-1] % mod;
    }

}
ll qpow(ll x,ll y,ll mod) { // 快速幂
    ll res = 1;
    while(y){
        if(y & 1) res = res * x % mod;
        x = x * x % mod;
        y >>= 1;
    }
    return res;
}
ll inv(ll n,ll mod) {  // 求逆元
    return qpow(n,mod-2,mod);
}

例题: 牛客挑战赛11 B 题

但是如果n, m 非常大了, 也就是达到1e18的那种, 那么我们就要上卢卡斯定理了, 并且T一般会比较少(<100).
同时如果mod数也非常的大, 那么在快速幂的同时还要套上快速乘才行, 防止爆….. 在线求.

//求a * b % mod; 由于这道题的mod都很大,所以乘法都容易爆,故需要快速乘.
ll mut_mod(ll a,ll b,ll mod)
{
    ll res = 0;
    while(b){
        if(b&1) res = (res + a) % mod;
        a = (a + a) % mod;
        b >>= 1;
    }
    return res;
}
//扩展GCD.
ll ex_gcd(ll a,ll b,ll &x,ll &y)
{
    if(!b){
        x = 1; y = 0;
        return a;
    }
    ll r = ex_gcd(b,a%b,x,y);
    ll tmp = x;
    x = y ; y = tmp - a/b*y;
    return r;
}
//快速幂用于求逆元,虽然ex_gcd也行,但是我不会用. xx.
ll qpow(ll x,ll y,ll mod)
{
    ll res = 1;
    while(y){
        if(y & 1) res = mut_mod(res,x,mod) % mod;
        x = mut_mod(x,x,mod) % mod;
        y >>= 1;
    }
    return res;
}
//n % mod 的逆元
ll inv(ll n,ll mod)
{
    return qpow(n,mod-2,mod);
}
//求n! % mod
ll fac(ll n,ll mod)
{
    ll res = 1;
    for(int i=2;i<=n;i++) res = res * i % mod;
    return res;
}
//C(n, m) % mod
ll Comb(ll n,ll m, ll mod)
{
    if(m>n) return 0;
    return fac(n,mod) * inv(fac(m,mod),mod) % mod * inv(fac(n-m,mod),mod) % mod;
}
//卢卡斯定理,求C(n,m)%mod,且n,m,mod都很大.
ll Lucas(ll n, ll m,ll mod){
    return m ? Lucas(n/mod, m/mod, mod) * Comb(n%mod, m%mod,mod) % mod : 1;
}
// 要求mod为质数.

题目就不找了……

你可能感兴趣的:(错排_组合数学_卢卡斯定理)