ST表好难!!!!!(而且也没那么有用)
倍增思想与 ST 表(稀疏表)
一、倍增思想
1. 核心概念
倍增(Binary Lifting)也叫二进制拆分法:将较大的步数/长度拆成若干个 2 的整数次幂之和,每次跳跃 2^k 步,大幅减少操作次数。
任意正整数可唯一表示为 2^0 + 2^1 + 2^2 + ... 的形式,例如:
5 = 2^2 + 2^0
7 = 2^2 + 2^1 + 2^0
2. 为什么要用倍增?
- 普通遍历:一步一跳,复杂度 O(n)
- 倍增法:按 2^k 大步跳跃,复杂度 O(log n)
倍增可用于:快速幂、树上最近公共祖先(LCA)、二分优化、ST 表等。
3. 通用流程
- 预处理:计算出所有 2^0, 2^1, 2^2, ..., 2^(log n) 层级的数据。
- 从大到小尝试:从最大的幂次开始,能跳就跳,不能跳就减小幂次。
- 完成目标:直到覆盖整个区间或达到目标状态。
二、ST 表(Sparse Table)
1. 适用场景
ST 表用于静态数组的区间最值查询(RMQ):
- 静态:数组不发生修改(不支持单点/区间更新)
- 查询:多次询问区间 [l, r] 的最大值或最小值
- 复杂度:预处理 O(n log n),单次查询 O(1)
对比:暴力 O(n),线段树 O(log n),ST 表查询最快。
2. 数组定义
设原数组为 a[1..n](下标从 1 开始)。
定义二维数组 st[k][i] 表示:从位置 i 开始,长度为 2^k 的区间的最值。
- k = 0:长度 1,st[0][i] = a[i]
- k = 1:长度 2,st[1][i] = max(a[i], a[i+1])
- k = 2:长度 4,st[2][i] = max(a[i..i+3])
3. 预处理(动态规划 + 倍增)
(1) 初始条件 (k=0)
st[0][i] = a[i],其中 1 ≤ i ≤ n
(2) 递推公式 (k ≥ 1)
长度为 2^k 的区间可以拆成两个长度为 2^(k-1) 的子区间:
- 左半:起点 i,长度 2^(k-1) → st[k-1][i]
- 右半:起点 i+2^(k-1),长度 2^(k-1) → st[k-1][i+2^(k-1)]
取最大值:st[k][i] = max( st[k-1][i], st[k-1][i+2^(k-1)] )
边界条件:i + 2^k - 1 ≤ n
(3) 预处理 log 数组
为了快速得到 k = floor(log2(len)),提前计算 log2 数组:
4. 区间查询
查询 [l, r] 的最值:
- 区间长度 len = r - l + 1
- 取 k = log2[len]
- 用两个长度为 2^k 的区间覆盖 [l, r]:
- 第一个区间:[l, l+2^k-1] → st[k][l]
- 第二个区间:[r-2^k+1, r] → st[k][r-2^k+1]
- 结果:ans = max( st[k][l], st[k][r-2^k+1] )
两个区间允许重叠,因为最值运算满足可重复贡献性质。
示例
数组 a = [1,3,5,2,4],查询 [2,4](元素 3,5,2):
- len = 3,k = floor(log2 3) = 1,2^k = 2
- 第一段:st[1][2] → 区间 [2,3] 的最大值 5
- 第二段:st[1][3] → 区间 [3,4] 的最大值 5
- 答案 = max(5,5) = 5
三、完整代码(C++)
版本1:区间最大值
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
const int MAXN = 100010;
const int LOG = 20; // 2^20 > 1e5
int st[LOG][MAXN];
int log2n[MAXN];
vector<int> a;
void init(int n) {
// 预处理 log2
log2n[1] = 0;
for (int i = 2; i <= n; ++i)
log2n[i] = log2n[i / 2] + 1;
// 初始化 k=0
for (int i = 1; i <= n; ++i)
st[0][i] = a[i];
// 递推
int max_k = log2n[n] + 1;
for (int k = 1; k <= max_k; ++k) {
for (int i = 1; i + (1 << k) - 1 <= n; ++i) {
int mid = i + (1 << (k - 1));
st[k][i] = max(st[k-1][i], st[k-1][mid]);
}
}
}
int query(int l, int r) {
int k = log2n[r - l + 1];
return max(st[k][l], st[k][r - (1 << k) + 1]);
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n, m;
cin >> n >> m;
a.resize(n + 1);
for (int i = 1; i <= n; ++i)
cin >> a[i];
init(n);
while (m--) {
int l, r;
cin >> l >> r;
cout << query(l, r) << '\n';
}
return 0;
}

浙公网安备 33010602011771号