zoukankan      html  css  js  c++  java
  • 扩展卢卡斯

    $ C_n^m mod p ,p不一定为质数$

    根据唯一分解定理

    [p = p_1^{a_1}p_2^{a_2}dots p_k^{a_k} ]

    得到(k)个互质的(p_i^{a_i}),满足

    [left{egin{array}{ll}C_n^m mod p_1^{a_1}\C_n^m mod p_2^{a_2}\dots\end{array} ight. ]

    最后用中国剩余定理合并求得最小(x)解即可

    那么问题转换为求(C_n^m mod p^k,[p∈prime])

    此时问题等价于

    [frac{n!}{m!(n-m)!}mod p^k ]

    那么只需要求(m!(n-m)!)(p^k)的逆元就可以了

    但是可能不存在逆元,因为(p^k)不一定为质数,而对于非质数的模采用扩展欧几里得求解(frac{1}{a}mod p),但需要满足(gcd(a,p) = 1)

    转换式子

    [frac{frac{n!}{p^x}}{frac{m!}{p^y}frac{(n-m)!}{p^z}}p^{x - y -z}mod p^k ]

    其中(x)(n!)(p)因子的个数,(y)(m!)(p)因子的个数,(z)((n-m)!)(p)因子的个数

    那么如果对于一个n,可以求出(frac{n!}{p^x}),就可以求出逆元了

    此时问题等价于

    [frac{n!}{p^x}mod p^k ]

    (n! = 1⋅2⋅3...⋅n = (p⋅2p⋅3pdots)(1⋅2dots))

    那么原式(= p^{lfloorfrac{n}{p} floor}(1⋅2⋅3dots)(1⋅2⋅dots) \ = p^{lfloorfrac{n}{p} floor}({lfloorfrac{n}{p} floor})!prod_{i = 1,i otequiv0(mod p)}^ni)

    (mod p)是由循环节的

    [= p^{lfloorfrac{n}{p} floor}({lfloorfrac{n}{p} floor})!(prod_{i = 1,i otequiv0(mod p)}^{p^k}i)^{frac{n}{p^k}}(prod_{i = p^klfloorfrac{n}{p^k} floor,i otequiv0(mod p)}^n i) ]

    其中((prod_{i = 1,i otequiv0(mod p)}^{p^k}i))是循环节(1∼p)中所有无p因子数的乘积。

    ((prod_{i = p^klfloorfrac{n}{p^k} floor,i otequiv0(mod p)}^n i))是余项的乘积

    比如$22! mod 3^2 $

    (22! equiv (3⋅6⋅9⋅12⋅15⋅18⋅21)(1⋅2⋅4⋅dots)(mod 3^2) \ equiv 3^7(1⋅2⋅3⋅4⋅5⋅6⋅7)(1⋅2⋅4⋅5⋅7⋅8)(10⋅11⋅13⋅14⋅16⋅17)(19⋅20⋅22)(mod 3^2)\ equiv 3^7(1⋅2⋅3⋅4⋅5⋅6⋅7)(1⋅2⋅4⋅5⋅7⋅8)^2(19⋅20⋅22)(mod 3^2))

    (f(n) = frac{n!}{p^x})

    [f(n) = f(lfloorfrac{n}{p} floor)(prod_{i = 1,i otequiv0(mod p)}^{p^k}i)^{lfloorfrac{n}{p^k} floor}(prod_{i = p^klfloorfrac{n}{p^k} floor,i otequiv0(mod p)}^n i)\f(0) = 1 ]

    那么求(f(n))的时间复杂度是(O(log_pn))

    那么原式转换为

    [frac{f(n)}{f(m)f(n-m)}p^{x-y-z} mod p^k ]

    (f(m),f(n-m))一定与(p^k)互质,就可以用扩展欧几里得求逆元了

    而对于(p^{x - y - z})

    比如求(f(n) = frac{n!}{p^x})里的(x)

    (g(n))表示(n!)中有多少个p的因子,也就是(x)

    (g(n))满足递推式

    [g(n) = lfloorfrac{n}{p} floor + g(lfloorfrac{n}{p} floor)\g(n) = 0(n < p) ]

    就可以用时间复杂度为(O(log_pn ))求出

    答案就是

    [frac{f(n)}{f(m)f(n-m)}p^{g(x) - g(y) - g(z)} mod p^k ]

    模板

    时间复杂度(O(plogp))

    所以(p ≤1e6),n和m可以很大

    #include <iostream>
    #include <cstdio>
    #define ll long long
    using namespace std;
    void ex_gcd(ll a, ll b, ll &d, ll &x, ll &y){
        if(!b){
            d = a, x = 1, y = 0;
            return;
        }
        ex_gcd(b, a % b, d, y, x);
        y -= x * (a / b);
    }
    ll inv(ll a, ll p){//求a在p模下的逆元
        ll d, x, y;
        ex_gcd(a, p, d, x, y);
        return (x % p + p) % p;//保证有解了
    }
    ll pow(ll a, ll b, ll p){
        ll ans = 1; a %= p;
        while(b){
            if(b & 1)ans = ans * a % p;
            b >>= 1;
            a = a * a % p;
        }
        return ans;
    }
    ll crt(ll a, ll M, ll m){//x = a(mod m)
        return inv(M / m, m) * (M / m) * a % M;
    }
    ll fac(ll n, ll p, ll pk){//f(n)
        if(!n)return 1;
        ll ans = 1;
        for(ll i = 1; i <= pk; i++)//循环节
            if(i % p) ans = ans * i % pk;
        ans = pow(ans, n / pk, pk);
        for(ll i = pk * (n / pk); i <= n; i++)//余项
            if(i % p) ans = ans * (i % pk) % pk;
        return ans * fac(n / p, p, pk) % pk;
    }
    ll C(ll n, ll m, ll p, ll pk){
        ll N = fac(n, p, pk), M = fac(m, p, pk), Z = fac(n - m, p, pk);
        ll sum = 0;
        for(ll i = n; i; i = i / p) sum += i / p;//g(n)
        for(ll i = m; i; i = i / p) sum -= i / p;//g(m)
        for(ll i = n - m; i; i = i / p) sum -= i / p;//g(n-m)
        return N * pow(p, sum, pk) % pk * inv(M, pk) % pk * inv(Z, pk) % pk;
    }
    ll exLucas(ll n, ll m, ll p){//C(n,m) % p
        ll ans = 0;
        ll t = p;
        for(ll i = 2; i * i <= p; i++){
            ll k = 1;
            while(t % i == 0) k *= i,t /= i;
            ans = (ans + crt(C(n, m, i, k), p, k)) % p;
        }
        if(t > 1)
            ans = (ans + crt(C(n, m, t, t), p, t)) % p;
        return ans;
    }
    int main(){
        ll n, m, p;
        scanf("%lld%lld%lld", &n, &m, &p);
        printf("%lld
    ", exLucas(n, m, p));
        return 0;
    }
    

    对于(p)很大的数,可以先将p分解成几个较小的质数乘积,再利用中国剩余定理求解

  • 相关阅读:
    ACM进阶计划
    《算法竞赛入门经典》习题——Chapter 3
    js运算符
    Javascript的数据类型简述
    JS事件处理和事件对象
    对一道代码的看法
    SOA不是Web Service
    梳理一下最近要重点好学的东西
    ReportViewer使用手册
    Lesson 9
  • 原文地址:https://www.cnblogs.com/Emcikem/p/12462322.html
Copyright © 2011-2022 走看看