Sparse Table讲解


            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
#include
#include
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



你可能感兴趣的:(dp,algorithm,位运算,Data,Structure)