贴一发上官松柏大犇写的博客。
ST表(Sparse Table)是能够区间查询最值的数据结构,它采用倍增与dp的思想进行维护。
ST表复杂度:
- 空间复杂度 O(nlogn)
- 预处理时间复杂度 O(nlogn)
- 查询时间复杂度 O(1)
ST表优势:
- ST表的价值在于用较大的空间复杂度换取的 O(1) 的查询复杂度,与线段树 O(n) , O(n) , O(logn) 不同,ST表的查询部分并入主要部分程序后整体复杂度不会比线段树高。所以是对程序效率的有效优化。
ST表劣势:
- 有 O(nlogn) 的较大空间复杂度。
- 更新操作比较困难(即一般不支持更新,只能静态查询)。
预处理操作:
如果我们把每个k值当做一层,我们要考虑如何把 f[k][i] 用上一层的数据更新覆盖。采用倍增的思想,我们定义:
- f[k][i] 表示区间 [i,i+2k−1] (持续 2k 个)的最值。
显然上一层的覆盖范围正好是 f[k][i] 的一半,即 2k−1 。那么我们就可以用两段刚好覆盖住的区间 [i,2k−1−1] 和 [i+2k−1,(i+2k−1)+2k−1−1] 来收集结果了:
- f[k][i]=min{f[k−1][i],f[k−1][i+2k−1]}
查询操作:
同理,我们需要两个已知的固定区间,能够覆盖(可以重合一部分)整个查询区间。为了方便查找,我们规定这两个已知区间覆盖的区间长度均为2^k,并且两个区间分别有一端为L和R。那么随着这个k不断增大,一定存在一个临界点k,刚好覆盖(或最少重合)这个区间。
当k满足题意时,两个区间分别为 [L,L+2k−1] 和 [R−(2k−1),R] ,此时只需要满足 R−(2k−1)≤L+2k−1 即可,化简之后推出公式:
- R−L+2≤2k+1,k≥log2(R−L+2)−1
小常数优化:
由于系统计算log非常慢,所以我们可以参考这篇文章对 2i 的处理方法,把所有数值的log2()全部打表出来。这里的时间复杂度只需要 O(n) 。
int f[S][M],Log2[M],a[M];
void init(int n){//ST表的预处理
int s=0;
for(int i=1;i<=n;i++){
f[0][i]=a[i];
if(i&(i-1))Log2[i]=s;//删除最后一位二进制,如果刚好i=1<<t则变成0
else Log2[i]=s++;
}
for(int k=1;(1<<k)<=n;k++)
for(int i=1;i+(1<<k)-1<=n;i++)
f[k][i]=min(f[k-1][i],f[k-1][(1<<k-1)+i]);
}
int query(int L,int R){//ST表的查询
int k=Log2[R-L+2]-1;
return min(f[k][L],f[k][R-(1<<k)+1]);
}