作用:
ST算法是用来对任意一个数组,求其任以(l~r)区间内的最大值(最小值也可以)。
题解:
引出:
首先遇到这一类题,我们可以暴力查找,但是这种方法如果询问多次,直接让您原地起飞,显然我们机智的"前人"是不能忍的,于是牛逼的ST算法出现了。
其方法主要是以
O
(
N
l
o
g
N
)
O(N log_{} N)
O(NlogN)的时间,最后以
O
(
1
)
O(1)
O(1)时间回答。
题解:
设f[i][j]表示子区间 ( i , i + 2 j − 1 ) (i,i + 2^{j} - 1) (i,i+2j−1)中的最大值。我们可以倍增区间长度,那么则有 f [ i ] [ j ] = m a x ( 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 + 2 j − 1 ) (i——i + 2^{j} - 1) (i——i+2j−1)区间最大值=将长度为 2 j 2^{j} 2j分成两半: ( i — — i + 2 j − 1 ) (i——i+2^{j - 1}) (i——i+2j−1)与 ( i + 2 j − 1 (i+2^{j - 1} (i+2j−1—— i i i + 2 j 2^{j} 2j - 1)中间取最大。可以想成递推的方式吧。
初始化:
f[i][0]即是指
i
i
i~
i
i
i最大值=a[i];
输出:
假设我们需要查询区间[l,r]中的最小值,令
k
=
l
o
g
2
(
r
−
l
+
1
)
k=log2(r−l+1)
k=log2(r−l+1),则最小值为
m
a
x
(
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
+
2
k
−
1
)
(l, l + 2^{k} - 1)
(l,l+2k−1)和
(
r
−
2
k
+
1
,
r
)
(r - 2^{k} + 1, r)
(r−2k+1,r)所以我们只需要证明这两个区间覆盖了
l
—
—
r
l —— r
l——r及证
r
−
2
k
+
1
<
=
l
+
2
k
−
1
r - 2^{k} + 1 <= l + 2^{k} - 1
r−2k+1<=l+2k−1,这里用移项然后再带入k最后的到
r
−
l
>
=
0
r-l>=0
r−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;
}