作用
ST表的优势在于可以把查询一个区间的最值的时间复杂度降为 O ( 1 ) O(1) O(1)。
状态转移
假设有一个数组里有10000个元素,第i个元素记为ar[i]。
我们用f[i][j]表示从第i个元素开始,向右i+2j-1的 这些元素的最大值。
有f[i][j]=max(f[i][j-1],f[i+2j-1][j-1])
这其实是把区间[i][j]分成了两个部分,每部分的长度均为2j-1,将两个部分的最大值保存在f[i][j]中。有了这个状态转移方程后,就可以求出所有的f数组了。不难看出,时间复杂度为
O
(
n
l
o
g
n
)
O(nlogn)
O(nlogn)。
for (j = 1; j <= lg[n]; j++)
{
for (i=1;(i+(1<<(j-1)))<=n;i++)
{
f[i][j] = max(f[i][j - 1], f[i + (1 << (j - 1))][j - 1]);
}
}
如何查询
这里的思想非常巧妙。设查询的区间为[L][R]
则查询区间的长度为len=R-L+1
查询结果为max(f[L][
l
o
g
(
l
e
n
)
log(len)
log(len)],f[R-2len+1][
l
o
g
(
l
e
n
)
log(len)
log(len)]
通过两次分别向左,向右的查询,可以巧妙的覆盖整个查询区间。由于
l
o
g
log
log经常需要调用,需要预处理一下。
l
g
[
i
]
=
l
g
[
i
/
2
]
+
1
lg[i]=lg[i/2]+1
lg[i]=lg[i/2]+1
用代码实现的话,就是
while (q--)
{
int l, r, len,ans;
scanf("%d%d", &l, &r);
len = r - l + 1;
ans = max(f[l][lg[len]], f[r - (1 << lg[len]) + 1][lg[len]]);
printf("%d\n", ans);
}
这样,通过预处理 O ( n l o g n ) O(nlogn) O(nlogn)后,每次查询的时间复杂度仅为 O ( 1 ) O(1) O(1)。是不是很快呢?