RMQ
所谓 R M Q ( R a n g e M a x i m u m / M i n i m u m Q u e r y ) RMQ(Range ~~Maximum/Minimum~~ Query) RMQ(Range Maximum/Minimum Query),即区间最值查询问题。该问题的解决方法有线段树, S T ST ST表等等。对于静态的 R M Q RMQ RMQ, S T ST ST表是最佳的选择。但是 S T ST ST表无法解决动态的区间最值而线段树可以
Sparse Table
初始化
设数组
a
a
a是所求的序列,
d
p
[
i
]
[
j
]
dp[ i ] [ j ]
dp[i][j]表示从第
i
i
i个数开始连续
2
j
2^j
2j个数的最小值。刚开始
d
p
[
i
]
[
0
]
dp[ i ][ 0 ]
dp[i][0] 初始化为
a
[
i
]
a[ i ]
a[i],因为
2
j
2^j
2j一定是偶数,因此对于每一个
d
p
[
i
]
[
j
]
dp[i][j]
dp[i][j],我们将它分为如下图所示的两部分:
因此状态转移方程为:
d
p
[
i
]
[
j
]
=
m
a
x
{
d
p
[
i
]
[
j
−
1
]
,
d
p
[
i
+
(
1
<
<
(
j
−
1
)
]
[
j
−
1
]
}
dp[ i ][ j ] = max \{ dp[ i ][ j - 1 ] , dp[ i+(1<<(j-1) ][ j - 1 ] \}
dp[i][j]=max{dp[i][j−1],dp[i+(1<<(j−1)][j−1]}
查询
假如查询的区间为
[
L
,
R
]
[ L , R ]
[L,R],由于我们只保存
[
L
,
L
+
(
1
<
<
?
)
]
[ L , L+(1<<?) ]
[L,L+(1<<?)]的最值,因此我们需要找到一个最大整数数
k
k
k使得
(
1
<
<
k
)
<
=
R
−
L
+
1
(1<<k)<=R-L+1
(1<<k)<=R−L+1,然后我们查询两个个可能有重复的区间:
[
L
,
L
+
(
1
<
<
k
)
]
[ L , L+(1<<k) ]
[L,L+(1<<k)] 和
[
R
−
(
1
<
<
k
)
+
1
,
R
]
[ R-(1<<k)+1 , R ]
[R−(1<<k)+1,R] 。如下图所示:
例如查询区间
[
1
,
6
]
[ 1 , 6 ]
[1,6],先查询了
[
1
,
4
]
[ 1 , 4 ]
[1,4],再查询
[
3
,
6
]
[ 3 , 6 ]
[3,6],这样合并之后的最大值仍然是所求区间的最大值
时间复杂度
初始化的时间复杂度为 O ( n l o g n ) O(nlogn) O(nlogn),查询的时间复杂度 O ( 1 ) O(1) O(1)
代码
以洛谷ST表模板为例:
update:2020.9.18
之前查询时是 l o g 2 n log_2n log2n查询的,现在在洛谷会被卡,于是更改成了预处理,实际上注释的部分就是对区间长度求 l o g 2 ( r − l + 1 ) log_2(r-l+1) log2(r−l+1)
int d[maxn][25], a[maxn], Log[maxn];
int n, m;
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 << 1) + (x << 3) + (ch ^ 48);
ch = getchar();
}
return x * f;
}
inline void write(int a) {
if (a < 0) putchar('-'), a = -a;
if (a >= 10) write(a / 10);
putchar(a % 10 + 48);
}
void init() {
for (int i = 1; i <= n; i++) d[i][0] = a[i];
for (int j = 1; (1 << j) <= n; j++) {
for (int i = 1; i + (1 << j - 1) <= n; i++) //注意这里是1<<j-1而不是1<<j
d[i][j] = max(d[i][j - 1], d[i + (1 << j - 1)][j - 1]);
}
for (int i = 1; i <= n; i++) {
Log[i] = log2(i);
}
}
int query(int l, int r) {
/*int k = 0;
while (1 << (k + 1) <= r - l + 1) k++;*/
int k = Log[r - l + 1];
return max(d[l][k], d[r - (1 << k) + 1][k]);
}
int main() {
//freopen("in.txt","r",stdin);
//freopen("out.txt","w",stdout);
//ios_base::sync_with_stdio(0), cin.tie(0), cout.tie(0);
n = read(), m = read();
//scanf("%d%d",&n,&m);
for (int i = 1; i <= n; i++) {
a[i] = read();
//scanf("%d",&a[i]);
}
int l, r;
init();
while (m--) {
l = read(), r = read();
//scanf("%d%d",&l,&r);
write(query(l, r));
putchar('\n');
}
return 0;
}