【知识点】ST表
引入
ST表(Sparse Table, 稀疏表)是一种数据结构,用于解决可重复贡献问题(RMQ)。
什么是可重复贡献问题?
对于一串数字,需要计算某个区间的数字依次进行某个二元运算最终得到的结果。如果这个运算符合幂等律(\(x * x = x\))和结合律(\((x * y) * z = x * (y * z)\)),那么它就有可重复贡献的性质。
可重复贡献:即使区间内某些数重复参与运算,也不影响结果。
举个例子,求最大值运算\(max(x, y)\),符合幂等律:\(max(x, x) = x\), 符合结合律:\(max(max(x, y), z) = max(x, max(y, z))\),则求最大值运算具有可重复贡献的性质。如果要计算下标从\(1\)到\(12\)的区间的最大值,那么计算从\(1\)到\(8\)的最大值\(a\),再计算从\(5\)到\(12\)的最大值\(b\),则最终结果为\(max(a, b)\)。在这个过程中,即使从\(5\)到\(8\)的区间被重复计算,也不影响最终答案。
常见的可重复贡献问题有:区间最值、区间按位和、区间按位或、区间GCD等。
原理
构建
ST表基于倍增的思想。我们需要计算以第\(i\)个位置为左端点,长度为\(2^j\)的区间的计算结果。
令\(st[i][j]\)表示以第\(i\)个位置为左端点,长度为\(2^j\)的区间的计算结果。
状态转移方程为:\(st[i][j] = st[i][j-1] * st[i + 2^{j-1}][j-1]\)。
查询
如果需要查询\([l, r]\)区间的计算结果,需要先计算区间长度\(len = r - l + 1\)。然后计算出\(log_2len\)向下取整,\(2^{log_2len}\)作为被查询的区间长度。一般计算\(log_2len\)是被预处理的,可以保证查询的低时间复杂度。
举个例子,查询\(1\)到\(12\)的计算结果,先计算长度\(len = 12\),然后查出\(log_2len = log_212 = 3\),则\(2^{log_2len} = 2^3 = 8\)为被查询区间的长度,我们只需要查询\(1\)到\(8\)的结果和\(5\)到\(12\)的结果,然后进行运算即可。
代码实现
构建
for (int i = 1; i <= n; i++)
cin >> st[i][0];
for (int j = 1; j <= logn[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]); //倍增递推
构建的时间复杂度为\(O(nlogn)\)。
查询
//预处理logn
logn[1] = 0, logn[2] = 1;
for (int i = 3; i <= n; i++)
logn[i] = logn[i / 2] + 1;
//接收查询
cin >> l >> r;
len = r - l + 1; //计算区间长度
ans = max(st[l][logn[len]], st[r - (1 << logn[len]) + 1][logn[len]]);
//注意两个区间的下标
预处理的时间复杂度\(O(n)\)。
查询的时间复杂度为\(O(1)\)。
模板题AC代码
AC代码
#include <bits/stdc++.h>
using namespace std;
using i64 = long long;
int n, m, st[100100][35], logn[100100], l, r, len;
inline void init() {
logn[1] = 0, logn[2] = 1;
for (int i = 3; i <= n; i++)
logn[i] = logn[i / 2] + 1;
return;
}
int main() {
ios::sync_with_stdio(false);
cin.tie(0), cout.tie(0);
cin >> n >> m;
init();
for (int i = 1; i <= n; i++)
cin >> st[i][0];
for (int j = 1; j <= logn[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--) {
cin >> l >> r;
len = r - l + 1;
cout << max(st[l][logn[len]], st[r - (1 << logn[len]) + 1][logn[len]]) << '\n';
}
return 0;
}

浙公网安备 33010602011771号