Sparse Table(ST表)
ST表是解决 RMQ (Range Minimum/Maximum Query) 静态区间最值查询问题的离线数据结构。其预处理时间复杂度为 O(nlogn) ,查询时间复杂度为 O(1),n为序列长度。RMQ 问题也可以用线段树来解决,只不过线段树的查询复杂度为 O(nlogn) 而且代码量大。
本文以求最小值为例。
算法
- 算法利用了二分和动态规划的思想:
设 st[i][j] 表示区间 [i,i+2^j-1] 内的最小值。
例:对于序列 3,2,4,5,6,8,1,2,9,7
则:st[1][0]=3,st[1][1]=2,st[1][2]=2,st[1][3]=1 。
也就是第 i 个数起,连续 2^j 个数中的最小值。
- st[i][j] 的转移方程:
st[i][j] = min( st[i][j-1] ,st[i+(1<<(j-1))][j-1] )
注意其中 j 是阶段,i 是状态,所以循环中 j 放在外层。
void init_ST()
{
for(int j=1;j<=floor(log(n)/log(2));j++)
for(int i=1;i+(1<<j)-1<=n;i++)
ST[i][j] = min(ST[i][j-1], ST[i+(1<<(j-1))][j-1]);
}
预先需要令 st[i][0]=a[i] 。 a[] 是原序列,这个式子同样符合 st 的定义。
- 查询
查询时,用两个长度为 2^ k 的区间覆盖待查询区间,而长度为 2^ k 的区间最值可以直接从 st数组 中得到。
其中 k = floor(log(b-a+1)/log(2))
即 2^k = b-a+1 向下取整。
区间 [a,b] 的最小值为 min(st[a][k],st[b-2^k+1][k])
init_ST();
for(int i=1;i<=M;i++)
{
int a, b, k, ans;
scanf("%d%d", &a, &b); //输入查询区间
k = floor(log(b-a+1)/log(2));
ans = min(ST[a][k], ST[b-(1<<k)+1][k]);
printf("%d\n", ans);
}
例题
模板: 洛谷P3865 【模板】ST表.
另一个模板,我自己用线段树做了一遍:洛谷P2880 [USACO07JAN]Balanced Lineup G.