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

时间: 2024-08-02 09:11:39

Sparse Table讲解的相关文章

图论学习十之Sparse table

ST表 无修改区间最值询问问题 • 给出一个数组a[1..N] • Q个询问,每次询问每次询问区间[x,y]最大值. • N,Q <= 100000 St表的递推 • 用st[k][i]表示从i开始连续2^k个元素的最值. • 得出递推式 • st[k+1][i]=max(st[k][i], st[k][i+2^k]);(假设求最大值) • 从小到大枚举k,再枚举i. • 那么预处理出上面这个数组的复杂度为O(nlogn) .   询问操作 • 找出最大的k使得 2^k<=y-x+1 • 注意

ST算法(Sparse Table)

RMQ问题: 给定一个序列,每次询问一个区间最小值 / 最大值. 没有修改. //拿区间最大值来举例. memset(ans, -INF, sizeof(ans)); for (int i = 1; i <= n; i++) ans[i][0] = a[i]; for (int j = 1; (1<<j) <= n; j++) //枚举长度为2^j的区间 for (int i = 1; i+(1<<j)-1 <= n; i++) //枚举区间起点 ans[i][j

RMQ_第一弹_Sparse Table

title: RMQ_第一弹_Sparse Table date: 2018-09-21 21:33:45 tags: acm RMQ ST dp 数据结构 算法 categories: ACM 概述 RMQ (Range Minimum/Maximum Query) 从英文便可以看出这个算法的主要是询问一个区间内的最值问题,,, 暑假集训的时候学习了 线段树 ,,, 也可以对给定数组查询任意区间的最值问题,,,, 这两个主要的区别就是 线段树 可以进行单点的修改操作,,,而 Sparse Ta

转 acm历程 置顶帖

作者情况和我差不多 期望可以激励自己 转 http://blog.csdn.net/yueqiq/article/details/7623176 首先,我想说的就是,我是一个很普通的ACMer,高中没有参加过任何计算机和数学竞赛的经历,也没有ben那样过人的天资,努力至今也未能取得什么成绩,我之所以写下这篇文章,只是希望给刚进大学或者刚进ACM队的同学一点小小的帮助,希望你们可以少走一些弯路,更希望你们可以帮助华理取得我没能取得的辉煌. (1).起步阶段我是从大二开始接触ACM的,要说基础的话就

RMQ算法 (ST算法)

 概述: RMQ(Range Minimum/Maximum Query),即区间最值查询,是指这样一个问题:对于长度为n的数列A,回答若干询问RMQ(A,i,j)(i,j<=n),返回数列A中下标在i,j之间的最小/大值.对于一次查询,可以暴力地O(n),但是当查询次数很多的时候,这样的暴力就无法进行了.这时我们可以通过RMQ算法来解决这个问题. RMQ(ST):(关于学习RMQ的博客:框架即讲解比较详细 , 具体代码比较好) ST(Sparse Table)算法是一个非常有名的在线处理RMQ

RMQ问题 - ST表的简单应用

2017-08-26 22:25:57 writer:pprp 题意很简单,给你一串数字,问你给定区间中最大值减去给定区间中的最小值是多少? 用ST表即可实现 一开始无脑套模板,找了最大值,找了最小值,分别用两个函数实现,实际上十分冗余 所以TLE了 之后改成一个函数中同时处理最大值和最小值,就可以了 AC代码如下: /* @theme:poj 3264 @writer:pprp @declare:ST表(sparse table)稀疏表,用动态规划的思想来解决RMQ问题: @date:2017

区间最值ST算法

题目描述 给出一大串数字(编号为1到N),给定M个询问,每次询问两个数字A,B,要求A到B这段区间内的最大数. 输入输出格式 输入格式: 一个整数N表示数字的个数,接下来一行为N个数.第三行读入一个M,接下来M行,每行都有两个整数A,B. 输出格式: 输出共M行,每行输出一个数. 输入输出样例 输入样例#1: 6 34 1 8 123 3 2 4 1 2 1 5 3 4 2 3 输出样例#1: 34 123 123 8 说明 对于30%的数据,1<=N<=10000,1<=M<=1

51nod 1174 1174 区间中最大的数

题目链接:51nod 1174 1174 区间中最大的数 ST(Sparse Table)算法学习参考博客:http://blog.csdn.net/niushuai666/article/details/6624672 O(nlogn)预处理,O(1)查询 1 #include<cstdio> 2 #include<algorithm> 3 #include<cmath> 4 using namespace std; 5 const int N = 10001; 6

log2取整效率测试

RMQ问题中有个ST算法,当然还有个标准算法.LCA问题可以转化为带限制的RMQ(RMQ+-1)问题来解决.我们姑且认为这些问题的时间复杂度是查询$O(1)$的.但是,注意到对于RMQ(/+-1)问题,这个问题有个长度的限制,我们记为n.那么对于每个查询,我们都要询问一个范围[L,R],1<=L<=R<=n.这个区间的长度为R-L+1.然后我们将原区间分成两个Sparse Table上的项,即长度为int_log2(R-L+1)-1的两个子区间求解min,即合并两个子区间的信息. 那么问