题解: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
【核心思想】
-
问题分析:给定 \(n\) 组 \((a_i, p_i)\),其中 \(p_i\) 为质数,求 \(a_i\) 模 \(p_i\) 的乘法逆元。若 \(a_i\) 是 \(p_i\) 的倍数,则逆元不存在。这是一个经典的数论问题,核心在于利用费马小定理将逆元计算转化为快速幂问题。
-
算法选择:
- 费马小定理:若 \(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\)
-
关键步骤:
- 读取输入:整数 \(n\)(查询次数),以及 \(n\) 组 \((a, p)\)
- 判断逆元是否存在:
- 若 \(a \bmod p == 0\),输出
impossible - 否则调用
qmi(a, p - 2, p)计算逆元并输出
- 若 \(a \bmod p == 0\),输出
- 快速幂函数
qmi(a, k, p):- 初始化
res = 1 - 当 \(k > 0\) 时循环:
- 若 \(k\) 的最低位为 \(1\):
res = res * a % p a = a * a % pk >>= 1
- 若 \(k\) 的最低位为 \(1\):
- 返回
res
- 初始化
-
时间/空间复杂度:
- 时间复杂度:\(O(n \cdot \log p_i)\)。单次逆元计算使用快速幂,时间复杂度为 \(O(\log p_i)\)
- 空间复杂度:\(O(1)\),仅使用常数额外变量
-
快速幂求逆元的核心思想:
- 费马小定理的应用:在模数为质数的条件下,将除法运算转化为乘法运算(乘以逆元),而逆元可以通过幂运算直接求得
- 快速幂加速:利用二进制快速幂将 \(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
浙公网安备 33010602011771号