题解:AcWing 876 快速幂求逆元

【题目来源】

AcWing:876 快速幂求逆元 - AcWing题库

【题目描述】

给定 \(n\)\(a_i,p_i\),其中 \(p_i\) 是质数,求 \(a_i\)\(p_i\) 的乘法逆元,若逆元不存在则输出 impossible

注意:请返回在 \(0\sim p-1\) 之间的逆元。

【输入】

第一行包含整数 \(n\)

接下来 \(n\) 行,每行包含一个数组 \(a_i,p_i\),数据保证 \(p_i\) 是质数。

【输出】

输出共 \(n\) 行,每组数据输出一个结果,每个结果占一行。

\(a_i\)\(p_i\) 的乘法逆元存在,则输出一个整数,表示逆元,否则输出 impossible

【输入样例】

3
4 3
8 5
6 3

【输出样例】

1
2
impossible

【核心思想】

  1. 问题分析:给定 \(n\)\((a_i, p_i)\),其中 \(p_i\) 为质数,求 \(a_i\)\(p_i\) 的乘法逆元。若 \(a_i\)\(p_i\) 的倍数,则逆元不存在。这是一个经典的数论问题,核心在于利用费马小定理将逆元计算转化为快速幂问题。

  2. 算法选择

    • 费马小定理:若 \(p\) 是质数且 \(a\) 不是 \(p\) 的倍数,则 \(a^{p-1} \equiv 1 \pmod{p}\)。两边同乘 \(a^{-1}\)\(a^{p-2} \equiv a^{-1} \pmod{p}\),即 \(a\) 的逆元为 \(a^{p-2} \bmod p\)
    • 快速幂求模:使用二进制快速幂在 \(O(\log p)\) 时间内计算 \(a^{p-2} \bmod p\)
    • 逆元存在性判断:当且仅当 \(\gcd(a, p) = 1\) 时逆元存在。由于 \(p\) 是质数,只需判断 \(a \bmod p \neq 0\)
  3. 关键步骤

    • 读取输入:整数 \(n\)(查询次数),以及 \(n\)\((a, p)\)
    • 判断逆元是否存在
      • \(a \bmod p == 0\),输出 impossible
      • 否则调用 qmi(a, p - 2, p) 计算逆元并输出
    • 快速幂函数 qmi(a, k, p)
      • 初始化 res = 1
      • \(k > 0\) 时循环:
        • \(k\) 的最低位为 \(1\)res = res * a % p
        • a = a * a % p
        • k >>= 1
      • 返回 res
  4. 时间/空间复杂度

    • 时间复杂度:\(O(n \cdot \log p_i)\)。单次逆元计算使用快速幂,时间复杂度为 \(O(\log p_i)\)
    • 空间复杂度:\(O(1)\),仅使用常数额外变量
  5. 快速幂求逆元的核心思想

    • 费马小定理的应用:在模数为质数的条件下,将除法运算转化为乘法运算(乘以逆元),而逆元可以通过幂运算直接求得
    • 快速幂加速:利用二进制快速幂将 \(O(p)\) 的暴力计算降至 \(O(\log p)\),使大质数模下的逆元计算变得可行
    • 逆元与互质的关系:逆元存在的充要条件是 \(\gcd(a, p) = 1\)。当 \(p\) 为质数时,只要 \(a\) 不是 \(p\) 的倍数,逆元必然存在
    • 与扩展欧几里得算法的对比:费马小定理求逆元要求模数为质数,时间 \(O(\log p)\);扩展欧几里得算法适用于任意模数,时间 \(O(\log a)\),但代码稍复杂
    • 适用于模质数下的除法运算、分数取模、组合数计算(如阶乘逆元预处理)等场景

【算法标签】

快速幂

【代码详解】

#include <bits/stdc++.h>
using namespace std;

typedef long long LL;  // 定义长整型别名,防止乘法溢出

/**
 * 快速幂算法:计算 a^k mod p
 * @param a 底数
 * @param k 指数
 * @param p 模数
 * @return a^k mod p 的结果
 */
int qmi(int a, int k, int p)
{
    int res = 1;        // 初始化结果为1(乘法单位元)

    // 使用二进制分解指数k
    while (k)
    {
        // 如果当前二进制位为1,将a的当前幂次乘入结果
        if (k & 1)
        {
            res = (LL)res * a % p;  // 使用长整型防止乘法溢出
        }

        // 将a平方,准备处理下一位
        a = (LL)a * a % p;

        // 右移一位,处理下一个二进制位
        k >>= 1;
    }

    return res;
}

int main()
{
    int n;  // 测试用例数量
    scanf("%d", &n);

    // 处理每个测试用例
    while (n--)
    {
        int a, p;  // 输入参数:a为底数,p为模数
        scanf("%d%d", &a, &p);

        // 使用费马小定理计算a在模p下的乘法逆元
        // 逆元 = a^(p-2) mod p
        int res = qmi(a, p - 2, p);

        // 判断逆元是否存在:当且仅当a与p互质(即a不是p的倍数)
        if (a % p)
        {
            // 逆元存在,输出结果
            printf("%d\n", res);
        }
        else
        {
            // 逆元不存在(a是p的倍数)
            puts("impossible");
        }
    }

    return 0;
}

【运行结果】

3
4 3
1
8 5
2
6 3
impossible
posted @ 2026-06-23 11:24  团爸讲算法  阅读(5)  评论(0)    收藏  举报