Sparse Tabel名为稀疏表,又称为ST表,可以在O(1)的时间复杂度下完成查询区间最值,相比线段树和树状数
组,效率提升了不少.ST表本质上是一个很经典的dp,通过预处理完成O(1)的查询.既然是个dp,那我们来看下dp的
定义吧(下面以查询区间最大值为例).
dp[i][j]:表示以i为起点,长度为2^j的区间最值
那么我们很容易得出状态转移方程:dp[i][j] = max(dp[i][j - 1], dp[i + (1 << (j - 1))][j - 1]),这里解释下状态转移方程:
以i开头长度为2^j的区间最大值,可以由以i开头长度为2^(j - 1)最大值和以i + (1 << (j - 1))开头长度为2^(j - 1)的最大值
两者取大即可得出.从状态转移方程中可以看出大的状态j依赖于小的状态(j - 1),所以我们从小到大枚举j即可.j的下界
很容易得出是1(0的时候是做的dp初始化),关键是j的上界怎么确定?对于有n个元素的序列,j的上界为小于等于以2为
底n的对数(此处取整),如果此时j值比其他,那dp[i][j]的定义就已经没有意义了,这点是很好理解的.
查询区间[L, R]
ST表中如果要查询[L, R]的最值,我们可以先计算出以2为底(R - L + 1)的对数k,然后在dp[L][k]和dp[R - (1 << k)
+ 1][k]中取最值即可,此步骤需要的时间复杂度是O(1)的,所以说ST表支持O(1)查询最值.
下面是ST表的一份代码实现:
#include<stdio.h> #include<math.h> #include<algorithm> using namespace std; const int N = 1e+5; int st[N][20]; void init_st(int size) { for (int j = 1; 1 << j <= size; ++j) for (int i = 0; i + (1 << j) - 1 < size; ++i) st[i][j] = max(st[i][j - 1], st[i + (1 << (j - 1))][j - 1]); } int query(int L, int R) { int k = log(R - L + 1) / log(2.0); return max(st[L][k], st[R - (1 << k) + 1][k]); } int main() { int n, q; scanf("%d", &n); for (int i = 0; i < n; ++i) scanf("%d", &st[i][0]); init_st(n); scanf("%d", &q); while (q--) { int l, r; scanf("%d%d", &l, &r); printf("%d\n", query(l, r)); } return 0; }
从上面分析中,我们可以发现ST表不适合做动态更新,如果程序中需要成段更新或者单点更新,还是得用线段树
或者树状数组.不过对于静态数据来说,ST表无疑是个最佳选择,编码简单,而且查询效率也很高。
这里附上一道可以 供各位练手的题目:
http://codeforces.com/contest/488/problem/D