RMQ问题 st表详解【模板】【板题】POJ-3264 Balanced Lineup

RMQ问题

RMQ (Range Minimum/Maximum Query)问题是指:
对于长度为n的数列A,回答若干询问RMQ(A,i,j)(i,j<=n),返回数列A中下标在i,j里的最小(大)值,
也就是说,RMQ问题是指求区间最值的问题。
来自百度百科

简单说就是给出n个数的数组,每次询问给出LR,问在 a[L…R] 这个区间里的最值

ST表就是用来解决该问题
ST(Sparse Table)算法可以在O(nlogn)时间内进行预处理,然后在O(1)时间内查询每个问题;
但是ST只能解决静态区间查询,如果是动态的就没有办法了;
该算法本质是动态规划

预处理

定义st[i][j] 是从第i个数起的 2 ^ j 的最大值,即区间 [i,i+(2^j)-1] 的最大值;
我们来考虑动态转移方程
首先显然st[i][0]就等于a[i];
st[i][j]我们可以分成两部分一部分是st[i][j-1] 另一部分是st[i+2^j][j-1]
举例:
在这里插入图片描述
求st[1][3],即a[1…8]的最大值,我们把区间[1,8]分成两部分,st[1][2]和st[5][2],即a[1…4]和a[5…8],这样子st[1][3]的值就是st[1][2]和st[5][2]的最大值;
st[1][3] = max(st[1][2], st[5][2]) = max(5, 10) = 10;

那么我们就得到转移方程了
st[i][j] = max(st[i][j-1],st[i][i+2^j-1][j-1])

查询

预处理完,我们来学习如何通过st表来得到任意区间的最值
我们直接通过例子来学习
在这里插入图片描述
我们求[1,6]区间的最大值,我们预处理的st中是不包含这个区间的,但是我们可以通过两个不同区间的值得到;
虽然区间[1,6]的信息我们没有,但是我们有区间[1,4]和[3,6]的信息,即st[1][2]和st[3][2];
那么我们可以通过这两个区间的最大值直接得到[1,6]的最大值,尽管这两个区间是有重叠的,但是对我们求解最值没有影响,所以max(st[1][2],st[3][2]) 就是[1,6]的最大值;

通过这个例子我们得到st表查询区间的方法:
求区间 [L,R] 时,我们先求出不超过该区间长度且在st存在的区间大小即 k=log2(R-L+1);(向下取整),我们就得到需要的两个区间的长度;
我们再找以L为左边界,长度为2 ^ k的区间,即区间 [L,L+2^k-1];
和以R为右边界,长度为2 ^ k的区间,即区间 [R-2^k+1,R];
再st表中查询这两个区间,取两者的最大值,即:
st[i][j] = max(st[L][k], st[R - (2 ^ k) + 1][k])

模板

const int maxn = 4e6+5;

int st[maxn][20], a[maxn];

void build(int n) {
	for (int i = 1; i <= n; i++) {
		st[i][0] = a[i];
	}
	for (int j = 1; (1 << j) <= 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]);
		}
	}
}

int query(int l, int r) {
	int k = log2(r - l + 1);
	return max(st[l][k], st[r - (1 << k) + 1][k]);
}

板题

POJ-3264

题意

n个数,q次询问,每次给出L和R,问区间[L,R]的最大最小值的差

题解

两个st表,一个记录求最小值,一个求最大值即可

代码

/*
 * @author: arc
 * @date: 2020-08-15 18:56:28
 */
#include
#include
#include
using namespace std;
const int maxn = 5e4+5;

int stmax[maxn][20], stmin[maxn][20];

void build(int n) {
    for (int j = 1; (1 << j) <= n; j++) {
        for (int i = 1; i + (1 << j) - 1 <= n; i++) {
            stmax[i][j] = max(stmax[i][j - 1], stmax[i + (1 << (j - 1))][j - 1]);
            stmin[i][j] = min(stmin[i][j - 1], stmin[i + (1 << (j - 1))][j - 1]);
        }
    }
}

int query(int l, int r) {
    int k = log2(r - l + 1);
    return (max(stmax[l][k], stmax[r - (1 << k) + 1][k]) - min(stmin[l][k], stmin[r - (1 << k) + 1][k]));
}

int main(){
    int n, m;
    scanf("%d%d", &n, &m);
    for (int i = 1; i <= n; i++) {
        scanf("%d", &stmax[i][0]);
        stmin[i][0] = stmax[i][0];
    }
    build(n);
    int l, r;
    for (int i = 0; i < m; i++) {
        scanf("%d%d", &l, &r);
        printf("%d\n", query(l, r));
    }
    return 0;
}

你可能感兴趣的:(笔记)