快速幂模板

点击查看代码
/*
【倍增思想】快速幂(模板)

解法一:暴力循环      会超时

解法二:利用倍增 + 二进制
a^1 = a           ------1
a^2 = a^1 * a^1   ------2
a^4 = a^2 * a^2   ------4
a^8 = a^4 * a^4   ------8
a^16 = a^8 * a^8  ------16
a^32 = a^16 * a^16
倍增思想的本质:把指数拆成二进制
     任何整数 b 都可以拆成2 的幂次之和(二进制分解),比如:b=10 → 二进制 1010 → 10 = 8 + 2 = 2³ + 2¹因此 a^10 = a^(8+2) = a^8 * a^2(同底数幂相乘,指数相加)。
     结合你列的倍增公式:
     a^2 = a^1 * a^1(算 1 次)
     a^4 = a^2 * a^2(算 1 次)
     a^8 = a^4 * a^4(算 1 次)
     计算 a^10 只需算 a^2 * a^8,总共只需要 3 次倍增 + 1 次乘法,而暴力需要 10 次乘法。
3. 二进制分解的通用规则
     对任意指数 b:
     把 b 转成二进制,每一位对应「是否需要乘上当前的倍增项」;
     遍历 b 的二进制位,从低位到高位:
     如果当前位是 1,就把当前的倍增项乘到结果里;
     不管当前位是不是 1,都让倍增项自己倍增(a = a*a),对应二进制位权升级(2⁰→2¹→2²…);
     b 右移一位(b = b/2),处理下一位。


***核心逻辑:把指数拆成二进制,遍历二进制的每一位:如果是 1,就把当前的倍增项乘到结果里;
不管是不是 1,都让倍增项自己乘自己(翻倍),直到指数拆完。


取模运算的规则:
1.当计算过程中,只有“加法”和“乘法”时,取模可以放在任意的位置
            (a * b * c * d) % p = (a % p * b % p * c % p * d % p) % p = ((a * b % p * c) % p * d) % p
2.当计算过程中,存在“减法”时,结果是可能出现负数的,此时如果需要补正,就需要“模加模”的技巧来补正
            (a - b) % p   ==  ((a - b) % p + p) % p
3.当计算过程中,存在“除法”的时候,取模是会造成结果错误的
            解决:求逆元
*/

#include <iostream>

using namespace std;

typedef long long LL;

LL qpow(LL a, LL b, LL p){
    LL ret = 1; // 初始化结果为1(任何数的0次方是1,乘法的起点)
    while(b){   // 等价于 while(b > 0),指数b未拆完就继续循环
        // 步骤1:判断b的二进制最后一位是否为1(b&1是位运算,效率极高)
        if(b & 1) ret = ret * a % p; 
        // 步骤2:底数倍增(a = a^2),并取模避免溢出
        a = a * a % p;
        // 步骤3:b右移一位(等价于b = b / 2),去掉最后一位,处理下一位
        b >>= 1;
    }
    return ret; // 返回最终结果 (a^b) % p
}

int main(){
    LL a, b, p;
    cin >> a >> b >> p;
    printf("%lld^%lld mod %lld=%lld", a, b, p, qpow(a, b, p));
    return 0;
}

posted @ 2026-03-05 16:55  无酒无剑  阅读(39)  评论(0)    收藏  举报