ST 表 (RMQ)
💡 核心思想
ST 表(Sparse Table)用于静态区间最值查询(RMQ)。预处理 $O(n \log n)$,查询 $O(1)$。不支持修改。核心是倍增思想:$st[i][j]$ 表示从位置 $i$ 开始、长度为 $2^j$ 的区间的最值。
ST 表的优势是查询 $O(1)$,比线段树快。但前提是数据不能修改(静态)。如果需要修改,还是用线段树。ST 表的预处理是"从小区间推大区间",查询时用两个可能重叠的区间取最值(因为 min/max 运算允许重叠,这是 ST 表能 $O(1)$ 查询的关键)。
🎯 直觉理解
ST 表就像"预计算好的统计报表"。你对每种长度的区间都预先算好最值,查询时只需要查两张表取最大值。因为"两个可能重叠的区间取最大值"不影响结果(max 操作幂等),所以可以 $O(1)$ 查询。
📝 算法流程
- 预处理:$st[i][0] = a[i]$,$st[i][j] = \max(st[i][j-1], st[i+2^{j-1}][j-1])$
- 查询:找最大的 $k$ 使得 $2^k \le r-l+1$,取 $\max(st[l][k], st[r-2^k+1][k])$
- 不支持修改
$$st[i][j] = \max(st[i][j-1],\ st[i + 2^{j-1}][j-1])$$
$$\text{查询} [l,r]: \max(st[l][k],\ st[r-2^k+1][k]),\ k = \lfloor \log_2(r-l+1) \rfloor$$
📊 复杂度分析
| 指标 | 复杂度 |
|---|---|
| 时间 | 预处理 $O(n \log n)$,查询 $O(1)$ |
| 空间 | $O(n \log n)$ |
💻 参考实现(C++)
C++ (C++17)
#include <bits/stdc++.h>
using namespace std;
int st[100005][20], lg[100005];
int main() {
int n, m; cin >> n >> m;
for (int i = 1; i <= n; i++) cin >> st[i][0];
lg[1] = 0;
for (int i = 2; i <= n; i++) lg[i] = lg[i/2] + 1;
for (int j = 1; (1<<j) <= n; j++)
for (int i = 1; i + (1<<j) - 1 <= n; i++)
st[i][j] = max(st[i][j-1], st[i+(1<<(j-1))][j-1]);
while (m--) {
int l, r; cin >> l >> r;
int k = lg[r - l + 1];
cout << max(st[l][k], st[r-(1<<k)+1][k]) << "\n";
}
return 0;
}⚠️ 常见坑点
lg 数组要预处理避免每次调用 log 函数
ST 表不支持修改——需要修改时用线段树
查询时两个区间可以重叠(min/max 的幂等性)
空间 $O(n \log n)$ 可能很大
📚 相关题目
| 题目 | 来源 | 难度 | 备注 |
|---|---|---|---|
| P3865 ST表 | 洛谷 | CSP-S | ST表模板 |
| P2048 跳房子 | 洛谷 | CSP-S | ST表+其他算法 |