写在前面
- 在写题时我们常常会遇见 R M Q RMQ RMQ问题(区间最值),那么对于求解 R M Q RMQ RMQ问题,我们有多种解法,如线段树,单调队列等等,现在我要讲的是一种倍增解法即 S T ST ST算法。
ST讲解
- 给定一段序列 A A A, S T ST ST算法能在 O ( N l o g N ) O(NlogN) O(NlogN)的时间预处理后,以 O ( 1 ) O(1) O(1)的复杂度查询,在线回答在一段区间 l , r l, r l,r中最大(小)值是多少。
- 对于一段序列,我们可以选取一些 2 2 2的整数次幂来作为代表值从而划分整个区域。
- 设 F [ i , j ] F[i, j] F[i,j]表示序列 A A A在子区间 [ 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 + 2 j − 1 − 1 ] [ j − 1 ] ) F[i][j] = max( F[i][j-1], F[i + 2^{j-1} - 1][j - 1]) F[i][j]=max(F[i][j−1],F[i+2j−1−1][j−1])
- 递推边界为 F [ i , 0 ] = A [ i ] F[i,0] = A[i] F[i,0]=A[i]
- 当我们查询的时候延用刚才的思路,一个区间的最大值等于它两个左右子区间的最大值。询问区间 [ l , r ] [l,r] [l,r]时,我们选出一个数 t t t,满足 2 t < r − l + 1 ⩽ 2 t + 1 2^t <r-l+1 \leqslant 2^{t+1} 2t<r−l+1⩽2t+1,那么从 l l l开始的 2 t 2^t 2t个数到以 r r r结尾的 2 t 2^t 2t个数,一定覆盖整个区间 [ l , r ] [l,r] [l,r],这两段区间的最大值分别为 F [ l , t ] F[l,t] F[l,t], F [ r − 2 k + 1 , t ] F[r-2^k+1,t] F[r−2k+1,t],取最大值即可。
代码实现
预处理
void ST_prework() {
for (int i = 1; i <= n; ++i) f[i][0] = a[i];
for (int j = 1; j <= 20; ++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 ST_query(int l, int r) {
int _t = log(r - l + 1) / log(2);
return max(f[l][_t], f[r - (1 << _t) + 1][_t]);
}
例题
luogu P2251
在求解过程中把
m
a
x
max
max,改成
m
i
n
min
min即可