第63章 倍增法
倍增法(Binary Lifting)是一种基于"成倍增长"思想的高效算法,通过预处理将问题分解为多个的幂次规模的子问题,从而在查询时实现快速合并求解。该算法在时间复杂度上通常能将线性查询优化为对数级,广泛应用于数论(快速幂)、图论(树上倍增求祖先/LCA)、RMQ(ST 表)等领域。
63.1 倍增法的基本原理
63.1.1 核心思想
倍增法的核心是预处理出规模为的子问题的解,在查询时将原问题分解为若干个2的幂次对应的子问题,通过组合子问题的解得到原问题的答案。其本质是利用二进制表示的特性,将任意整数表示为若干个不重复的2的幂次之和,从而实现高效的分解与合并。
63.1.2 预处理与查询流程
预处理阶段:
- 确定需要预处理的最大幂次(为满足的最大整数,即)。
- 对于每个位置,预处理出以为起点、长度为区间的信息,记为。
- 预处理递推关系:
其中为合并两个子问题解的函数。
查询阶段:
- 将查询规模拆解为二进制:
- 依次处理每一个2的幂次子区间,通过函数合并结果得到最终答案。
63.1.3 时间复杂度
- 预处理:
- 单次查询:
因为任何正整数都能唯一表示为若干个不重复的 2 的幂次之和(二进制)。预处理出"长度为 1、2、4、8…"的子问题解后,任意长度都能被这些 2 的幂次"拼"出来,且拼出的段数 ≤ ,从而把查询降到对数级。
63.2 倍增法的典型应用:快速幂
快速幂是倍增法在数论中的经典应用,用于高效计算,避免朴素循环的时间开销。
原理: 将指数拆分为二进制,例如二进制为,等价,则。 提前递推,遍历二进制位,若当前位为1则累乘对应幂次,全程取模防止溢出。
实现代码
long long fastPower (long long a, long long n, long long m) {
long long result=1;
a %= m;
while (n>0){
if(n & 1){
result = (result * a) % m;
}
a = (a * a) % m;
n >>= 1;
}
return result;
}
## 63.3 真题示例(ST 表:区间最值 RMQ)
> **【GESP 八级风格·编程题】**
> **题目描述**:给定一个长度为 $$n$$ 的数列,之后有 $$q$$ 次询问,每次询问区间 $$[l, r]$$ 中的最大值。$$n, q \le 10^5$$。
> **输入格式**:第一行 $$n$$;第二行 $$n$$ 个整数;第三行 $$q$$;之后 $$q$$ 行每行 $$l, r$$(下标从 0 开始)。
> **输出格式**:每个询问输出一个最大值。
**思路**:用倍增预处理出"长度为 $$2^j$$ 的区间最值",查询时把 $$[l,r]$$ 拆成两段已预处理的长度 $$2^k$$ 区间,取 max 即可,单次查询 $$O(1)$$。
```cpp
#include <iostream>
#include <vector>
#include <cmath>
using namespace std;
const int MAXN = 100005;
int a[MAXN];
int st[MAXN][20]; // st[i][j] = 区间 [i, i + 2^j - 1] 的最大值
int n;
void build() {
for (int i = 0; i < n; ++i) st[i][0] = a[i]; // 长度 1 = 本身
for (int j = 1; (1 << j) <= n; ++j) // 长度 2^j
for (int i = 0; i + (1 << j) - 1 < n; ++i)
st[i][j] = max(st[i][j - 1],
st[i + (1 << (j - 1))][j - 1]); // 左右两半合并
}
int query(int l, int r) {
int k = (int)log2(r - l + 1); // 最大的 2^k ≤ 区间长度
return max(st[l][k], st[r - (1 << k) + 1][k]); // 两段覆盖整个区间
}
int main() {
cin >> n;
for (int i = 0; i < n; ++i) cin >> a[i];
build();
int q; cin >> q;
while (q--) {
int l, r; cin >> l >> r;
cout << query(l, r) << "\n";
}
return 0;
}
- ST 表:预处理 ,查询 ,但只能处理可重复贡献的问题(max/min/gcd),不支持单点修改;
- 若需要动态修改区间,要用线段树/树状数组(查询 )。 八级阶段,RMQ 静态最值优先掌握 ST 表。
练习题
下列选择题考查倍增法、快速幂与 ST 表,参考答案默认折叠,点击"参考答案"展开。
1.(复杂度) 用倍增法(如 ST 表)预处理长度为 的数组,时间复杂度是?
- A.
- B.
- C.
- D.
参考答案
B。倍增预处理要填 st[i][j]( 有 种, 有 种),故 。
2.(快速幂) 计算 的快速幂算法,时间复杂度是?
- A.
- B.
- C.
- D.
参考答案
B。快速幂把指数 按二进制拆分,循环次数 = 的二进制位数 = ,即 。
3.(快速幂细节) 快速幂代码中 if (n & 1) 的作用是?
- A. 判断 是否为 0
- B. 判断当前二进制位是否为 1,是则把对应幂次乘进结果
- C. 判断结果为奇数
- D. 用于取模
参考答案
B。n & 1 取 的最低二进制位;若该位为 1,说明当前这一"2 的幂次"需要乘入结果,随后 n >>= 1 处理下一位。
4.(ST 表) 关于 ST 表(倍增区间最值),下列说法正确的是?
- A. 支持区间修改
- B. 单次查询是
- C. 只能求区间和
- D. 预处理是
参考答案
B。ST 表查询 (两段预处理的 max 取较大);它不支持修改、只适合可重复贡献运算(max/min/gcd),预处理是 。
5.(思想) 倍增法的核心思想是?
- A. 每次把问题规模减半
- B. 把任意规模拆成若干个 2 的幂次子问题快速合并
- C. 用堆维护最优解
- D. 分治排序
参考答案
B。倍增的本质是"二进制拆分 + 预处理 2 的幂次子问题",从而把查询降到对数级。A 描述的是二分/分治的某一种,不如 B 准确。