OneCoder Avatar
OneCodercoderli.com · 938 篇博文
八级

C++ 算法考级专栏

真题分析、矩阵探测、递归回溯与基础语法

🎨 视觉封面

【GESP】C++八级考试大纲知识点梳理 (4) 倍增法

📅 2026-01-31·✍️ OneCoder·计算中...·⏱️ 12 分钟
#GESP#C++#考试大纲#倍增法

GESP C++ 八级考试大纲知识点梳理系列文章:

  1. 计数原理:加法与乘法
  2. 排列与组合
  3. 杨辉三角与组合数
  4. 倍增法
  5. 代数与平面几何 {: .prompt-tip}

继上一篇我们探讨了杨辉三角与组合数之后,我们继续深入 GESP C++ 八级大纲。今天的主角是算法竞赛中极其常用且高效的思想——倍增法

(4)掌握倍增法概念。了解倍增法的时间复杂度。 {: .prompt-info}

倍增法(Doubling Method)不仅仅是一个特定的算法,更像是一种“思想”。它的核心在于“成倍增长”,利用二进制的性质,将线性级别的处理转化为对数级别的处理,极大地优化了时间复杂度。

本人也是边学、边实验、边总结,且对考纲深度和广度的把握属于个人理解。因此本文更多的不是一个教程,而是个人知识梳理,如有遗漏、疏忽,欢迎指正、交流。 {: .prompt-warning}

一、什么是倍增法?

顾名思义,倍增就是“成倍增加”。

想象一下,你要从起点 A 走到终点 B。

  • 普通方法:一步一步走,每次走 1 米。如果距离是 NN 米,需要走 NN 步。时间复杂度 O(N)O(N)
  • 倍增方法:通过“预处理能力”,我们可以一次跳 1,2,4,8,16,,2k1, 2, 4, 8, 16, \dots, 2^k 米。
    • 如果距离是 15 米,我们可以先跳 8 米,再跳 4 米,再跳 2 米,再跳 1 米(15=8+4+2+115 = 8+4+2+1)。
    • 总共只需要跳 4 次,而不是 15 次。

核心原理

任何一个正整数 NN 都可以表示为若干个 22 的幂次之和(这就是二进制的本质)。 例如 19=16+2+1=(10011)219 = 16 + 2 + 1 = (10011)_2。 因此,我们可以通过组合这些 2k2^k 的跳跃,快速到达任何目标位置。

1.1 时间复杂度

倍增法的核心优势在于速度。 对于范围 NN,我们只需要考虑 20,21,,2k2^0, 2^1, \dots, 2^k (其中 2kN2^k \le N) 也就是 log2N\log_2 N 级别的数据。 因此,倍增法相关的查询操作通常具有 O(logN)O(\log N) 的时间复杂度。


二、倍增法的应用场景

快速幂 (Quick Pow)

这是倍增思想最基础、最典型的应用,用于快速计算 ab(modp)a^b \pmod p

1. 核心思想:二进制拆分

普通做法

写一个循环,执行 bb 次乘法。时间复杂度 O(b)O(b)。当 bb 很大(如 101810^{18})时,绝对会因超时(TLE)而得分 0。

倍增做法

利用 bb 的二进制表示,我们将 bb 拆解为若干个 2k2^k 的和。 例如计算 3133^{13}

13=(1101)_2=8+4+113 = (1101)\_2 = 8 + 4 + 1 所以:

313=3(8+4+1)=38×34×313^{13} = 3^{(8+4+1)} = 3^8 \times 3^4 \times 3^1

你会发现,每一项 31,32,34,383^1, 3^2, 3^4, 3^8 \dots 都是前一项的平方

  • 31=33^1 = 3
  • 32=(31)23^2 = (3^1)^2
  • 34=(32)23^4 = (3^2)^2
  • 38=(34)23^8 = (3^4)^2

我们只需要简单的 O(logb)O(\log b) 次乘法,就可以算出结果。

2. 代码示例 (C++)

CPP
/**
 * 快速幂计算 a^b % p
 * 时间复杂度:O(log b)
 * 空间复杂度:O(1)
 */
long long quick_pow(long long a, long long b, long long p) {
    long long ans = 1;      // 记录最终结果
    a = a % p;              // 预先取模,防止 a 很大导致溢出

    while (b > 0) {
        // 核心逻辑:判断 b 的二进制最后一位是 0 还是 1
        // 如果是 1 (即 b 为奇数),说明当前的权重 a 需要乘进结果里
        if (b & 1) {
            ans = (ans * a) % p;
        }

        // a 自身倍增:a^1 -> a^2 -> a^4 -> a^8 ...
        // 无论当前位是否为 1,权值 a 都要翻倍等待下一位使用
        a = (a * a) % p;

        // b 右移一位,相当于除以 2,处理下一位二进制
        b >>= 1;
    }
    return ans;
}

为了让大家更清晰地理解代码与上述数学原理的对应关系,我们可以看下面这个演示图:

  • b & 1 (取当前最低位):这是在检查 bb 的二进制表示中,当前处理到的这一位是 0 还是 1。
    • 如果是 1:说明指数 bb 包含当前的 2k2^k (例如 13=8+4+113 = 8+4+1 中的 11)。因此,我们需要把当前的底数 a (此时它的值是 a2ka^{2^k}) 乘进结果 ans 里。
    • 如果是 0:说明不需要这一项,直接跳过。
  • a = a * a (基数倍增):对应 a1a2a4a8a^1 \to a^2 \to a^4 \to a^8 \dots 的过程。无论 b 的当前位是不是 1,底数 a 都要不断翻倍,为下一位做准备。
  • b >>= 1 (右移):对应二进制位的遍历,从低位向高位移动。

演示:计算 313(modp)3^{13} \pmod p (13=11012)(13 = 1101_2)

循环轮次 当前 b (二进制) 当前底数 a 判断 b & 1 操作 ans 变化
初始 1101 (13) 313^1 乘入 ans 1×311 \times 3^1
第 1 轮 110 (6) 323^2 不乘 不变
第 2 轮 11 (3) 343^4 乘入 ans 31×343^1 \times 3^4
第 3 轮 1 (1) 383^8 乘入 ans 31×34×383^1 \times 3^4 \times 3^8
结束 0 - - - 3133^{13}

3. 其他应用拓展举例

快速幂不仅仅用于算“幂”,它的本质是“利用二进制加速递推”。常见的变形和应用包括:

(1) 模运算乘法逆元 (Modular Multiplicative Inverse) 在计算组合数 C(n,m)(modp)C(n, m) \pmod p 或有理数取模时,需要做除法 a/b(modp)a/b \pmod p。 但是模运算没有除法((a/b)%p(a%p/b%p)%p(a/b) \% p \neq (a\%p / b\%p) \% p)。 根据费马小定理,我们可以把除法转为乘法:

费马小定理:如果 pp 是质数,且 aa 不是 pp 的倍数,则 ap11(modp)a^{p-1} \equiv 1 \pmod p

符号解释:“\equiv” 是同余符号。这句话的通俗意思是:ap1a^{p-1} 除以 pp 的余数是 1

变形一下:a×ap21(modp)a \times a^{p-2} \equiv 1 \pmod p。 这说明 ap2a^{p-2} 就是 aa 在模 pp 意义下的倒数(即逆元)。 所以:a/b(modp)    a×bp2(modp)a / b \pmod p \iff a \times b^{p-2} \pmod p

即求 bbp2p-2 次方。这是快速幂最高频的使用场景

(2) 矩阵快速幂 (Matrix Exponentiation) 如果把数字 aa 换成一个矩阵 AA,把乘法换成矩阵乘法,原理一模一样。 这用于解决 O(N)O(N) 递推问题(如斐波那契数列、线性递推式)加速到 O(logN)O(\log N)

[Fn+1Fn]=[1110]n[F1F0]\begin{bmatrix} F_{n+1} \\ F_n \end{bmatrix} = \begin{bmatrix} 1 & 1 \\ 1 & 0 \end{bmatrix}^n \begin{bmatrix} F_1 \\ F_0 \end{bmatrix}

其中矩阵的 nn 次幂就用快速幂求解。

(3) 快速乘 (Quick Mul)a×b(modp)a \times b \pmod p 中,aabb都高达 101810^{18},直接相乘会爆 long long 范围。 我们可以把“乘法”看作是“加法的幂”,用类似快速幂的逻辑(把 xx 换成 ++): a×13=a×(8+4+1)=(a×8)+(a×4)+(a×1)a \times 13 = a \times (8+4+1) = (a \times 8) + (a \times 4) + (a \times 1)。 通过倍增 aa 来实现大数相乘取模。

(4) 字符串 Hash / 滚动 Hash 在计算字符串 Hash 值时,经常需要计算 Basek(modM)Base^k \pmod M,为了快速获取子串 Hash,需要快速幂预处理或实时计算 BaseBase 的幂。


三、总结

倍增法是解决很多“线性变对数”问题的金钥匙。

  • 核心:利用二进制拆分,将 NN 次操作压缩为 logN\log N 次。

掌握了倍增,你不仅能解决 8 级的考点,更是在向高级算法思维迈进了一大步。


💡 OneCoder 资源指引

所有代码开源上传至 GitHub:yummy-code 仓库 · GESP 专题站:GESP WIKI

🤝 技术交流与答疑

欢迎加入:C++ GESP/CSP 考级答疑群(688906745)Java/Python交流群(982860385),点击可直接加群。

📚

猜你想读 · 相关文章推荐

GESP 编程与算法 · 八级⏱️ 11 分钟

【GESP】C++八级考试大纲知识点梳理 (1) 计数原理:加法与乘法

GESP C++ 八级考试大纲知识点梳理系列文章: 1. 计数原理:加法与乘法 2. 排列与组合 3. 杨辉三角与组合数 4. 倍增法 5. 代数与平面几何 {: .prompt-tip} GESP C++八级考试大纲正式进入了组合数学的领域。作为八级的第一条考点,计数原理是整个排列组合、概率论乃至后续很多算法(如动态...

阅读全文 →
GESP 编程与算法 · 八级⏱️ 14 分钟

【GESP】C++八级考试大纲知识点梳理 (2) 排列与组合

GESP C++ 八级考试大纲知识点梳理系列文章: 1. 计数原理:加法与乘法 2. 排列与组合 3. 杨辉三角与组合数 4. 倍增法 5. 代数与平面几何 {: .prompt-tip} 继上一篇我们探讨了计数原理(加法与乘法原理)之后,GESP 八级考纲的第二个重要考点便是排列与组合。 (2)掌握排列与组合基础知识...

阅读全文 →
GESP 编程与算法 · 八级⏱️ 20 分钟

【GESP】C++八级考试大纲知识点梳理 (3) 杨辉三角与组合数

GESP C++ 八级考试大纲知识点梳理系列文章: 1. 计数原理:加法与乘法 2. 排列与组合 3. 杨辉三角与组合数 4. 倍增法 5. 代数与平面几何 {: .prompt-tip} 继上一篇我们探讨了排列和组合之后,GESP C++八级大纲的第三条考点非常经典,它是计算机算法(尤其是动态规划)的重要入门案例。...

阅读全文 →
OneCoder

OneCoder (lihongzheshuai)

一个中年人的自留地,记录学习 C++、GESP/NOI、Java、Python 与算法架构的心得体会。本站唯一网址:coderli.com

💬 读者留言与交流

0 条讨论
✨ 支持 Markdown 语法格式
还没有留言,快来成为第一个讨论者吧!