不能直接用pow(a,b)%m,因为pow返回double导致精度丢失,且a^b易溢出long long;必须用快速幂取模,即二进制拆分指数、每步模乘防溢,时间复杂度O(log b)。

为什么不能直接用 pow(a, b) % m
因为 pow 返回 double,精度丢失严重;而 a^b 在 b 稍大(比如 10⁶)时就会溢出 long long,根本算不出完整结果——取模必须在计算过程中做,不能等幂算完再模。
快速幂取模的核心逻辑:二进制拆分 + 模乘防溢
把指数 b 看作二进制,例如 b = 13 = 1101₂,那么 a^13 = a^8 × a^4 × a^1。每次把底数平方、检查当前位是否为 1,同时每一步都对 m 取模。
关键点是乘法中间结果可能超 long long 范围(比如 m 接近 10¹⁸,a 和 res 都接近 m,相乘就爆)。所以得用“模乘”避免溢出:
long long mul_mod(long long a, long long b, long long m) {
long long res = 0;
while (b) {
if (b & 1) res = (res + a) % m;
a = (a << 1) % m; // 注意:这里用加法代替乘法,防 a * 2 溢出
b >>= 1;
}
return res;
}-
a和b必须先对m取模(a %= m; b %= m;),否则初始值就超限 - 如果
m较小(如m ),可用 <code>(a * b) % m替代mul_mod,但不通用 - 当
m == 1时直接返回 0,避免除零或无意义运算
C++标准实现(含边界处理)
以下函数能安全处理 0 ≤ a, b, m ≤ 10¹⁸ 范围内的输入(m > 0):
立即学习“C++免费学习笔记(深入)”;
long long pow_mod(long long a, long long b, long long m) {
if (m == 1) return 0;
a %= m;
long long res = 1;
while (b > 0) {
if (b & 1) res = mul_mod(res, a, m);
a = mul_mod(a, a, m);
b >>= 1;
}
return res;
}-
b == 0时循环不执行,res保持为 1 —— 符合数学定义a^0 % m = 1(只要m > 1) - 不要用递归写法,栈深度可能达 60 层(
b最多 ~60 位),虽不炸但没必要 - 若编译器支持
__int128(如 GCC),且m ,可用 <code>(__int128)a * b % m替代mul_mod,更快更简洁
常见报错和调试线索
遇到结果错误或运行时异常,优先查这几点:
- 传入
m == 0→ 除零未定义行为(% 0),务必在函数开头断言或判空 - 忘记对
a取模,导致第一次mul_mod输入超限 - 误把
b当作有符号数处理,b & 1对负数失效(b应为非负) - 使用
int存a/b/m,但题目给的是 10¹⁸ 级数据 → 一律用long long - 本地测试时用小样例(如
a=2,b=10,m=1000)验证,再换大数据
最易被忽略的是模乘的实现方式:用位移加法模拟乘法看似绕,但在没有 __int128 或高精度库的场合,这是唯一可移植的保精度手段。


















