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;
}
posted @ 2024-07-30 21:46  懒羊羊爱吃灰太狼  阅读(16)  评论(0)    收藏  举报