对ST表进行一个简单的总结,它可以实现O(1)的静态区间查询,可以适用于查询操作频繁但数据不修改的场景
题目来源
https://www.luogu.com.cn/problem/P3865
题目介绍
给定一个长度为 N 的数列,和 M 次询问,求出每一次询问的区间内数字的最大值。
解题思路
我们可以每次都对区间进行一次遍历,但这样的复杂度过高,我们可以进行一边预处理,最后实现O(1)的查询,我们先设置一个f的二位数组.
当j==0时,我们将数组中第 i 个元素的值放到f[i][0]中,对于 j > 0 的情况,我们根据倍增思想,将长度为2^j的区间划分为两个长度为2^(j-1)的子区间,即 [i, i + 2^(j - 1) - 1] 和 [i + 2^(j - 1), i + 2^j - 1]
因此,f[i][j]可以通过max(st[i][j - 1], st[i + (1 << (j - 1))][j - 1]) 得到。
对于查询,我们可以设置一个p=log2(r-l+1),然后我们可以在[l,l+2^p-1]和[r-2^p+1,r]两个区间内找到我们所需要值,对其求最大即可。
解题代码
#include<iostream>
#include<algorithm>
#include<vector>
#include<map>
#include<set>
#include<queue>
#include<cstring>
#include<math.h>
using namespace std;
typedef long long ll;
const int N = 1e5 + 5;
ll f[N][30];
void ST(int xx) {
for (int i = 1; i <= xx; i++) {
cin >> f[i][0];
}
for (int i = 1; i <= 20; i++) {
for (int j = 1; j + (1 << i) - 1 <= xx; j++) {
f[j][i] = max(f[j][i - 1], f[j + (1 << (i - 1))][i - 1]);
}
}
}
ll check(int l,int r) {
ll p = log2(r - l + 1);
return max(f[l][p], f[r - (1 << p) + 1][p]);
}
void slove() {
int n, q;
cin >> n >> q;
ST(n);
while (q--) {
ll x, y;
cin >> x >> y;
cout << check(x, y) << "\n";
}
}
int main() {
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
int ttt = 1;
while (ttt--) {
slove();
}
}
与此同时,我们也可以求最小值,只需修改中间一段的代码
void ST(int xx) {
for (int i = 1; i <= xx; i++) {
cin >> f[i][0];
}
for (int i = 1; i <= 20; i++) {
for (int j = 1; j + (1 << i) - 1 <= xx; j++) {
f[j][i] = min(f[j][i - 1], f[j + (1 << (i - 1))][i - 1]);
}
}
}
ll check(int l,int r) {
ll p = log2(r - l + 1);
return min(f[l][p], f[r - (1 << p) + 1][p]);
}
总结
这题就是这样的,主要运用了倍增和预处理的思想。
--撰稿人viture