题目链接

【模板】非质模数下的乘法逆元

题目描述

给定正整数对 ,求 的乘法逆元。

需要注意的是,数据不保证 为质数。

解题思路

本题要求解线性同余方程 ,其中 就是 的乘法逆元。题目特别指出, 不一定是质数,这意味着我们不能使用费马小定理。

求解该方程的通用方法是使用 扩展欧几里得算法(Extended Euclidean Algorithm)

  1. 问题转化

    • 线性同余方程 可以等价地写成二元一次不定方程(丢番图方程)的形式:,其中 是一个整数。
    • 根据 裴蜀定理,这个方程有解的充要条件是 能够整除 。因为 都是正整数,所以这个条件就是 。即 必须互质。
  2. 扩展欧几里得算法

    • 扩展欧几里得算法是用来求解形如 的方程的一组整数解 的。
    • 对于本题,我们调用扩展欧几里得算法求解 。由于逆元存在的前提是 ,所以该算法实际上就是求解
    • 算法会返回一组解 。其中 就是我们要求的 的一个乘法逆元。
  3. 结果处理

    • 扩展欧几里得算法求出的解 可能是一个负数或大于 的数。
    • 为了得到最小的正整数解,我们需要将其转化到 的范围内。通用的转化公式是 (x % p + p) % p。这样可以确保无论 是正还是负,结果都是一个落在 范围内的正数。

综上,我们通过扩展欧几里得算法求解即可。该算法的复杂度为

代码

#include <iostream>

using namespace std;
using ll = long long;

// 扩展欧几里得算法,求解 ax + by = gcd(a, b)
ll exgcd(ll a, ll b, ll &x, ll &y) {
    if (b == 0) {
        x = 1;
        y = 0;
        return a;
    }
    ll d = exgcd(b, a % b, y, x);
    y -= (a / b) * x;
    return d;
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    int t;
    cin >> t;
    while (t--) {
        ll a, p;
        cin >> a >> p;
        ll x, y;
        exgcd(a, p, x, y);
        // 保证结果是最小正整数
        cout << (x % p + p) % p << "\n";
    }
    return 0;
}
import java.util.Scanner;

public class Main {
    // 扩展欧几里得算法,返回 {gcd(a, b), x, y}
    public static long[] exgcd(long a, long b) {
        if (b == 0) {
            return new long[]{a, 1, 0};
        }
        long[] res = exgcd(b, a % b);
        long d = res[0];
        long x = res[2];
        long y = res[1] - (a / b) * res[2];
        return new long[]{d, x, y};
    }

    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int t = sc.nextInt();
        while (t-- > 0) {
            long a = sc.nextLong();
            long p = sc.nextLong();
            long[] result = exgcd(a, p);
            long x = result[1];
            // 保证结果是最小正整数
            System.out.println((x % p + p) % p);
        }
    }
}
def exgcd(a, b):
    """扩展欧几里得算法,返回 (gcd(a, b), x, y)"""
    if b == 0:
        return a, 1, 0
    d, y, x = exgcd(b, a % b)
    y -= (a // b) * x
    return d, x, y

def main():
    t = int(input())
    for _ in range(t):
        a, p = map(int, input().split())
        gcd, x, y = exgcd(a, p)
        # 保证结果是最小正整数
        print((x % p + p) % p)

if __name__ == "__main__":
    main()

算法及复杂度

  • 算法:扩展欧几里得算法
  • 时间复杂度:对于每次查询,时间复杂度为
  • 空间复杂度:,来自于递归的栈深度。