1. 简介
ST表是求解RMQ问题的一种算法, RMQ问题是 Range Maximum(Minimum) Query 的缩写, 意思是查询区间最大(最小)值。
它使用倍增的思想和动态规划的原理, 优点是速度更快, 做到 O ( n log n ) O(n \log n) O(nlogn)的预处理速度, O ( 1 ) O(1) O(1)的单次查询速度。
ST算法是一个离线算法, 是指使用它的前提是没有修改操作。用于解决"可重复贡献问题", 是指每个元素会对答案贡献多次, 像求最大最小值计算多次也不会有影响, 可以使用ST算法, 但比如求和这类问题就不能使用了
2. ST算法流程
2.1 预处理
用
a
[
1...
n
]
a[1...n]
a[1...n]表示一组数据, 设
f
[
i
,
j
]
f[i, j]
f[i,j]表示从
a
[
i
]
a[i]
a[i]到
a
[
i
+
2
j
−
1
]
a[i+2^j-1]
a[i+2j−1]范围内最大值, 也就是从
a
[
i
]
a[i]
a[i]向后数
2
j
2^j
2j个数的最大值, 可以得到
f
[
i
]
[
0
]
=
a
[
i
]
f[i][0] = a[i]
f[i][0]=a[i]。
把
f
[
i
,
j
]
f[i, j]
f[i,j]范围两部分, 每部分的长度为
2
j
−
1
2^{j-1}
2j−1, 可以得到状态转移方程:
f
[
i
]
[
j
]
=
max
(
f
[
i
]
[
j
−
1
]
,
f
[
i
+
2
j
−
1
]
[
j
−
1
]
)
f[i][j] = \max(f[i][j-1], f[i+2^{j-1}][j-1])
f[i][j]=max(f[i][j−1],f[i+2j−1][j−1])
参考代码(下标从1开始):
void build(){
for(int i = 1;i <= n;i++){
f[i][0] = a[i];
}
for(int j = 1;(1 << j) <= n;j++){
for(int i = 1;i + (1 << j) - 1 <= n;i++){
f[i][j] = max(f[i][j - 1], f[i + (1 << j-1)][j - 1]);
}
}
}
注意: 加减的优先级大于位运算
2.2 询问
假设要询问区间
[
l
i
,
r
i
]
[l_i, r_i]
[li,ri]的最大值, 则先求出最大的
x
x
x, 满足
2
x
≤
r
i
−
l
i
+
1
2^x \leq r_i-l_i+1
2x≤ri−li+1, 那么区间
[
l
i
,
r
i
]
=
[
l
i
,
l
i
+
2
x
−
1
]
⋃
[
r
i
−
2
x
+
1
,
r
i
]
[l_i, r_i] = [l_i, l_i+2^x-1] \bigcup [r_i-2^x+1, r_i]
[li,ri]=[li,li+2x−1]⋃[ri−2x+1,ri]。
两个区间元素个数都为
2
x
2^x
2x, 所以
[
l
i
,
r
i
]
[l_i, r_i]
[li,ri]的最大值为
max
(
f
[
l
i
]
[
x
]
,
f
[
r
i
−
2
x
+
1
]
[
x
]
)
\max(f[l_i][x], f[r_i-2^x+1][x])
max(f[li][x],f[ri−2x+1][x]), 可以在
O
(
1
)
O(1)
O(1)内计算出来。
两个区间有交集, 但是对于求区间最值没有影响, 这就是ST算法只适用于求区间最值的原因
参考代码:
int find(int l, int r){
int k = log2(r - l + 1) //要用到<cmath>头文件
int ans = max(f[l][k], f[r - (1 << x) + 1][x]);
}
2.3 技巧
有一个小技巧(其实我老师不大支持):
c
m
a
t
h
cmath
cmath库中
l
o
g
2
log2
log2函数效率不高, 还可以用
O
(
N
)
O(N)
O(N), 递推出
1
1
1~
n
n
n这
n
n
n种区间长度对应的
k
k
k值, 设
l
o
g
[
d
]
log[d]
log[d]表示
⌊
l
o
g
2
d
⌋
\lfloor log_2 d \rfloor
⌊log2d⌋, 则
l
o
g
[
d
]
=
l
o
g
[
d
2
]
+
1
log[d] = log[\frac{d}{2}]+1
log[d]=log[2d]+1
参考代码:
log[0] = -1;
for(int i = 1;i <= n;i++){
log[i] = log(i / 2) + 1;
}
3. 例题
01 序列
稍微思考就可以应用ST表
Kolekcjoner Bajtemonów 2
稍微难一点
感谢收看, 国庆快乐