ST表
解决\(RMQ(Range\ Minimum/Maximum\ Query)\) 即区间最值查询问题
\(RMQ\)算法一般用较长时间做预处理, 时间复杂度为\(o(nlogn)\), 然后可以用\(o(1)\)的时间解决每一次查询
预处理部分
- 我们设二维数组 \(dp[i][j]\) 表示从第\(i\)位开始连续\(2^j\)个数中的最小值。例如:\(dp[2][1]\) 表示从第2位数开始连续两个数的最小值。
- 求 \(dp[i][j]\) 的时候可以把区间分为两个部分,第一个部分是从\(i\)到\(i+2^{j-1}-1\), 第二部分是从\(i+2^{j-1}\)到\(i+2^j-1\), 其原理就是根据二进制前一个数是后一个数的两倍,就可以把\(i\)到\(i+2^j-1\)这个区间通过\(2^{j-1}\)分成相同的两个部分。
- 那么转移方程就可以表示出来了: \(dp[i][j] = min(dp[i][j - 1],\ dp[i + (1 << j - 1)][j - 1])\)
那么代码也很容易写出来:
for(int j = 1; (1 << j) <= n; j ++) {
for(int i = 1; i + (1 << j) - 1 <= n; i ++) {
dp[i][j] = max(dp[i][j - 1], dp[i + (1 << j - 1)][j - 1]);
}
}
注意这里的 i 与 j 是不能互换位置的,因为这个状态方程的含义是:先更新每两个元素的最小值,然后通过每两个元素的最小值得到四个元素的最小值,以此获得更长的最值。
查询部分
如果查询区间为\([l, r]\), 令\(k = log2(r-l+1)\), 则区间\([l, r]\)的最小值为\(min(dp[l][k],\ dp[r-(1<<k)+1][k])\)
最后给出查询代码:
int l, int r;
cin >> l >> r;
int k = log2(r - l + 1);
cout << min(dp[l][k], dp[r - (1 << k) + 1][k]) << endl;
模板题:
P3865 【模板】ST 表
点击查看代码
#include <bits/stdc++.h>
using namespace std;
const int N = 1e5 + 10;
int a[N], dp[N][20];
inline int read()
{
int x=0,f=1;char ch=getchar();
while (ch<'0'||ch>'9'){if (ch=='-') f=-1;ch=getchar();}
while (ch>='0'&&ch<='9'){x=x*10+ch-48;ch=getchar();}
return x*f;
}
int main() {
int n = read(), m = read();
for(int i = 1; i <= n; i ++) {
a[i] = read();
dp[i][0] = a[i];
}
for(int j = 1; (1 << j) <= n; j ++) {
for(int i = 1; i + (1 << j) - 1 <= n; i ++) {
dp[i][j] = max(dp[i][j - 1], dp[i + (1 << j - 1)][j - 1]);
}
}
while(m --) {
int l = read(), r = read();
int k = log2(r - l + 1);
printf("%d\n", max(dp[l][k], dp[r - (1 << k) + 1][k]));
}
return 0;
}

浙公网安备 33010602011771号