跳到主要内容

AcWing 876. 快速幂求逆元

本节目标

在质数模数下用费马小定理计算乘法逆元。

题意与约束

给定 a 和题目保证为质数的 p,其中 p≤2×10^9,输出 a 在模 p 下的逆元;若不存在则输出 impossible

第一反应与瓶颈

普通除法不能直接搬进模运算:a / b mod p 不是先做整数除法。逐个尝试所有余数寻找 x 使 a·x ≡ 1 也需要线性时间。

数学关系与算法推导

p 是质数且 a mod p ≠ 0 时,费马小定理给出 a^(p-1) ≡ 1,因此 a^(p-2) 就是逆元。用快速幂在 O(log p) 时间计算;余数为零时没有逆元。

正确性依据

可逆条件满足时,a · a^(p-2) = a^(p-1) ≡ 1 (mod p),返回值定义上就是逆元。余数为零时任意乘积仍为零,不可能同余于一。

样例执行过程

4 mod 3 = 1,逆元为 16 mod 3 = 0,无论乘什么都不能得到 1,输出 impossible

代码实现

C++17
#include <iostream>
using namespace std;

long long powerModulo(long long base, long long exponent, long long modulus) {
long long result = 1;
base %= modulus;
while (exponent > 0) {
if ((exponent & 1) != 0) {
result = result * base % modulus;
}
base = base * base % modulus;
exponent >>= 1;
}
return result;
}

long long modularInverse(long long value, long long prime) {
if (prime <= 1) {
return -1;
}
value %= prime;
if (value < 0) {
value += prime;
}
if (value == 0) {
return -1;
}
return powerModulo(value, prime - 2, prime);
}

#ifndef ALGORITHM_TUTORIAL_NO_MAIN
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);

int count;
cin >> count;
while (count-- > 0) {
long long value, prime;
cin >> value >> prime;
long long inverse = modularInverse(value, prime);
if (inverse == -1) {
cout << "impossible\n";
} else {
cout << inverse << '\n';
}
}
return 0;
}
#endif

复杂度分析

每组时间 O(log p),额外空间 O(1)

边界与易错点

费马公式不是任意模数下的万能公式;这里依赖题目给出的质数模数,并先判断 a % p 是否为零。取模后的两个乘数都小于 2×10^9,故标准 C++17 的 long long 可安全保存中间乘积;更大模数需改用可移植模乘。核心函数用 -1 表示不存在,入口再转换为平台要求的 impossible

模式迁移

模数是质数且分母非零时,组合数中的除法也可改写为乘逆元。回到欧拉函数与模运算