逆元模版大全

扩展欧几里得求逆元

1、时间复杂度:\(O(logn)\)

2、推理:

  • \(ax \equiv g \ (mod \ p)\),所以可以转化为:\(ax+py=g\)

  • 又因为由 \(exgcd\) 求出来的 \(d\)\(gcd(a,b)\),所以得满足 \(d \ | \ g\) ,才能有解。

  • 这里令 \(g=1\),则 \(x\) 为我们所求的 \(a\) 的逆元 \(a^{-1}\),所以我们需要满足 \(gcd(a,p)=1\),才能求出 \(a\) 的逆元 \(a^{-1}\),否则无解。

template<typename T>
struct Inv{
    inline void exgcd(T a, T &x, T &y, T p = 1e9 + 7) {
        if (!p) {
            x = 1;
            y = 0;
            return;
        }
        
        exgcd(p, y, x, a % p);
        y -= a / p * x;
    }
    
    inline T get_inv(T a, T p = 1e9 + 7) {// 逆元x,需要满足__gcd(a, p)==1
        T x, y;
        exgcd(a, x, y, p);
        x = (x % p + p) % p;
        return x;
    }
};

Inv<i64> t;

快速幂 + 费马小定理求逆元:时间复杂度为:\(O(logn)\)

  • 由于 \(gcd(a,p)=1\),则由费马小定理得:\(a^{p-1} \equiv 1 \ (mod \ p)\),又 \(ax \equiv 1 \ (mod \ p)\),则 \(x \equiv a^{-1}a^{p-1} \ (mod \ p) \Rightarrow x \equiv a^{p-2} \ (mod \ p)\)
template<typename T>
struct Inv{
    inline T qp(T a, T b, T p = 1e9 + 7) {
        a %= p;
        T res = 1;
        while (b) {
            if (b & 1) res = res * a % p;
            a = a * a % p;
            b >>= 1;
        }
        return res;
    }
    
    inline T get_inv(T a, T p = 1e9 + 7) {
		return qp(a, p - 2, p);
	}
};

Inv<i64> t;

线性预处理 \(1-1e7\) 以内单个数的逆元,整体时间复杂度为:\(O(n)\)

  • 具体推导过程:我们令 \(f(i)=i^{-1} \ mod \ p\) 表示 \(i\) 的逆元,令 $k=\left \lfloor \frac{p}{i} \right \rfloor $,然后有 \(p=ki+j\),其中 \(j=p \ mod \ i\),然后我们可以得到:\(ki+j \equiv 0 \ (mod \ p)\) ,我们让两边同时乘上 \(i^{-1}j^{-1}\),可以得到 \(i^{-1} \equiv -kj^{-1} \ (mod \ p)\),然后我们将前面的式子带入这个式子中,可以得到:\(i^{-1} \equiv (-\left \lfloor \frac{p}{i} \right \rfloor \ (p \ mod \ i)^{-1}) \ (mod \ p)\)

  • 模版:洛谷P3811

template<typename T>
struct Inv{
    int n;
    vector<T> inv;
    Inv() {}
    Inv(int n_, T p = 1e9 + 7) : inv(n_ + 1) {
        n = n_;
        inv[1] = 1LL;
        for (T i = 2; i <= n; i++) inv[i] = (p - p / i) * inv[p % i] % p;
        // 如果inv[i]=0,表示i在模p意义下没有逆元
    }
    
    inline T get_inv(T a) {
		return inv[a];
	}
};

Inv<i64> t;

递归求单个数的逆元,时间复杂度:\(O(n^{\frac{1}{3}})\)

template<typename T>
struct Inv{
    inline T get_inv(T n, T p = 1e9 + 7) {
        if (n == 1) return (T)1;
        return (p - p / n) * get_inv(p % n, p) % p;
    }
};

Inv<i64> t;

线性求任意给定的 \(n\) 个数的逆元,时间复杂度:\(O(n)\)

template<typename T>
struct Inv{
    inline T qp(T a, T b, T p = 1e9 + 7) {
        a %= p;
        T res = 1;
        while (b) {
            if (b & 1) res = res * a % p;
            a = a * a % p;
            b >>= 1;
        }
        return res;
    }
	
    inline T get_inv(T a, T p = 1e9 + 7) {
		return qp(a, p - 2, p);
	}
	
    int N;
    vector<T> a;
    vector<T> fac;
    vector<T> inv;
    Inv(int N_, vector<T> &a_, T p = 1e9 + 7) : fac(N_ + 1), inv(N_ + 1) {
        a = a_;
        N = N_;
        fac[0] = 1;
        for (T i = 1; i <= N; i++) fac[i] = fac[i - 1] * a[i] % p;
        T ans = get_inv(fac[N], p);
        for (T i = N; i >= 1; i--) {
            inv[i] = ans * fac[i - 1] % p;
            ans = ans * a[i] % p;
        }
    }
};
posted @ 2024-08-30 11:14  grape_king  阅读(55)  评论(0)    收藏  举报