作用:
ST算法是用来对任意一个数组,求其任以(l~r)区间内的最大值(最小值也可以)。
题解:
引出:
首先遇到这一类题,我们可以暴力查找,但是这种方法如果询问多次,直接让您原地起飞,显然我们机智的"前人"是不能忍的,于是牛逼的ST算法出现了。
其方法主要是以O(NlogN)O(N log_{} N)O(NlogN)的时间,最后以O(1)O(1)O(1)时间回答。
题解:
设f[i][j]表示子区间(i,i+2j−1)(i,i + 2^{j} - 1)(i,i+2j−1)中的最大值。我们可以倍增区间长度,那么则有f[i][j]=max(f[i][j−1],f[i+(1<<(j−1))][j−1])f[i][j] = max(f[i][j - 1], f[i + (1<<(j - 1))][j-1])f[i][j]=max(f[i][j−1],f[i+(1<<(j−1))][j−1]),即是(i——i+2j−1)(i——i + 2^{j} - 1)(i——i+2j−1)区间最大值=将长度为2j2^{j}2j分成两半:(i——i+2j−1)(i——i+2^{j - 1})(i——i+2j−1)与(i+2j−1(i+2^{j - 1}(i+2j−1——iii + 2j2^{j}2j - 1)中间取最大。可以想成递推的方式吧。
初始化:
f[i][0]即是指iii~iii最大值=a[i];
输出:
假设我们需要查询区间[l,r]中的最小值,令k=log2(r−l+1)k=log2(r−l+1)k=log2(r−l+1),则最小值为max(f[l][k],f[r−(1<<k)+1][k])max(f[l][k], f[r - (1 << k) + 1][k])max(f[l][k],f[r−(1<<k)+1][k])
证明:
更据前面分析我们知道,两个数组表示(l,l+2k−1)(l, l + 2^{k} - 1)(l,l+2k−1)和(r−2k+1,r)(r - 2^{k} + 1, r)(r−2k+1,r)所以我们只需要证明这两个区间覆盖了l——rl —— rl——r及证r−2k+1<=l+2k−1r - 2^{k} + 1 <= l + 2^{k} - 1r−2k+1<=l+2k−1,这里用移项然后再带入k最后的到r−l>=0r-l>=0r−l>=0,及原式成立。
代码:
#include <iostream>
#include <cstdio>
#include <cmath>
#include <algorithm>
using namespace std;
const int MAXN = 1e6 + 5;
int n, m, x, y, a[MAXN], f[MAXN][105];
void ST(int n) {
for(int i = 1;i <= n; i++) {
f[i][0] = a[i];
}
int t = log(n) / log(2) + 1;
for(int j = 1;j < t; j++) {
for(int i = 1; i <= n - (1 << j) + 1; i++) {
f[i][j] = max(f[i][j - 1], f[i + (1<<(j-1))][j - 1]);
}
}
}
int STF(int l, int r) {
int k = log(r - l + 1) / log(2);
return max(f[l][k], f[r - (1 << k) + 1][k]);
}
int main() {
scanf("%d %d", &n, &m);
for(int i = 1;i <= n; i++) {
scanf("%d", &a[i]);
}
ST(n);
for(int i = 1;i <= m; i++) {
scanf("%d %d", &x, &y);
printf("%d\n", STF(x, y));
}
return 0;
}