ST 表是用于解决可重复贡献问题的离线数据结构。
用 ST 表求解需要运算满足结合律,不支持修改操作,因为每次修改后都要重新预处理。
若题目是可重复贡献问题,且运算满足结合律、无需修改操作,则建议使用 ST 表。
可重复贡献问题是指对于运算 $\mathrm {opt} $ ,满足 ,则对应的区间询问就是一个可重复贡献问题。
若不满足,则求值时采用的预处理区间重叠,会导致重叠部分被计算两次,产生错误。
ST 表基于倍增思想,可以 预处理, 回答每个询问。
可重复贡献问题就算用来求解的预处理区间有重叠部分,只要这些区间的并是所求的区间,最终计算出的答案就是正确的,可使用至多两个预处理过的区间来覆盖询问区间。
# 实现
令 表示区间 中 运算的结果,
显然 区间中的最大值 ,
根据定义式,第二维就相当于倍增的时候「跳了 步」,
故状态转移方程为 。
查询时,对于询问 ,将其分为两个完全覆盖其而不超出其的区间 和 ,其中 ,
回答为 。
RMQ 模板
#include <bits/stdc++.h> | |
using namespace std; | |
const int logn = 21, maxn = 2000001; | |
int f[maxn][logn + 1], Logn[maxn + 1]; | |
int read() // 快读 | |
{ | |
ll x = 0; bool s = 0; char c = getchar(); | |
while(c < '0' || c > '9') {if(c=='-') s = 1; c = getchar();} | |
while('0' <= c && c <= '9') {x = (x << 3) + (x << 1) + (c ^ 48); c = getchar();} | |
return s ? -x : x; | |
} | |
void pre() // 准备工作,初始化 Logn 数组 | |
{ | |
Logn[1] = 0; Logn[2] = 1; | |
for(int i = 3; i < maxn; i++) Logn[i] = Logn[i / 2] + 1; | |
} | |
int main() | |
{ | |
int n = read(), m = read(); // 输入数据总数、询问次数 | |
for (int i = 1; i <= n; i++) f[i][0] = read(); // 数据输入加初始化 | |
pre(); | |
for (int j = 1; j <= logn; j++) | |
for (int i = 1; i + (1 << j) - 1 <= n; i++) // 注意边界 | |
f[i][j] = max(f[i][j - 1], f[i + (1 << (j - 1))][j - 1]); // ST 表的预处理,实质是动态规划 | |
for (int i = 1; i <= m; i++) | |
{ | |
int l = read(), r = read(); | |
int s = Logn[r - l + 1]; // 注意 r - l + 1 才为区间长度 | |
printf("%d\n", max(f[l][s], f[r - (1 << s) + 1][s])); // 输出结果,分别以左右两个端点为基础,向区间内跳 2 ^ s 的最大值 | |
} | |
return 0; | |
} |