ST表
概览与说明
1、 设计目标
ST表(Sparse Table)用于高效解决静态区间最值查询(RMQ)问题,通过倍增思想实现 O ( n l o g n ) O(n log n) O(nlogn) 预处理和 O ( 1 ) O(1) O(1)单次查询,适合处理不修改数据的场景。
2、 预处理与存储结构
- 定义二维数组
st[i][j]
,表示从位置 i i i 开始、长度为 2 j 2^j 2j 的区间的最值(如最大值)。 - 递推关系: s t [ i ] [ j ] = m a x ( s t [ i ] [ j − 1 ] , s t [ i + 2 j − 1 ] [ j − 1 ] ) st[i][j] = max(st[i][j-1], st[i + 2^{j-1}][j-1]) st[i][j]=max(st[i][j−1],st[i+2j−1][j−1]),即通过合并两个相邻子区间的最值得到更大区间的最值。
3、 对数表优化
预处理log_table[i]
数组,快速计算区间长度对应的指数
k
k
k ( 满足
2
k
≤
l
e
n
2^k ≤ len
2k≤len ),避免重复调用
l
o
g
log
log 函数,提升查询效率。
ST表的实现代码
1、 预处理Log表
int log_table[MAXN];
void init_log() {
log_table[1] = 0;
for (int i = 2; i < MAXN; i++)
log_table[i] = log_table[i/2] + 1;
}
2、 构建ST表
int st[MAXN][21]; // 第二维大小设为log2(MAXN)+1
void build(int a[], int n) {
for (int i = 1; i <= n; i++) st[i][0] = a[i]; // 初始化长度为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]);
}
3、 区间查询
int query(int l, int r) {
int k = log_table[r - l + 1];
return max(st[l][k], st[r - (1 << k) + 1][k]);
}
关键细节、优化及说明
细节处理:
- 边界处理
- 构建时需保证 i + ( 1 < < j ) − 1 < = n i + (1 << j) - 1 <= n i+(1<<j)−1<=n ,防止数组越界。
- 查询时通过 r − ( 1 < < k ) + 1 r - (1 << k) + 1 r−(1<<k)+1 确保分割后的区间覆盖原区间且不越界。
-
空间与时间权衡
-
空间复杂度为 O ( n l o g n ) O(n log n) O(nlogn) ,适用于数据规模较大的场景。
-
与线段树( O ( n ) O(n) O(n) 空间)相比,ST表查询更快但不支持动态修改。
-
典型应用场景:
-
静态区间最值(RMQ问题):
如洛谷P3865,直接应用模板查询最大值。
-
扩展操作:
- 区间最小值:将
max
替换为min
。 - 区间GCD/按位与/或:利用可重复贡献性质,修改合并逻辑。
- 区间最小值:将
-
高维RMQ:
二维ST表预处理子矩阵最值,查询时分解为四个子矩阵。
对比其他数据结构:
结构 | 预处理时间 | 查询时间 | 动态更新 | 适用场景 |
---|---|---|---|---|
ST表 | O ( n l o g n ) O(n log n) O(nlogn) | O ( 1 ) O(1) O(1) | 不支持 | 静态区间最值 |
线段树 | O ( n ) O(n) O(n) | O ( l o g n ) O(log n) O(logn) | 支持 | 动态区间操作 |
单调队列 | O ( n ) O(n) O(n) | O ( 1 ) O(1) O(1) | 支持 | 滑动窗口最值 |
练习题目推荐:
-
模板题
-
变种应用
- CF803G Periodic RMQ Problem(结合动态开点)
- 洛谷P1816 忠诚(区间最小值)