ST 表是个好东西,虽然前些天 ldq 学长已经讲完啦,但是那天他讲了那么多,让智商受限的我完全没有全部接受,选择性的扔掉了一部分(其实不舍的扔,记不住QAQ)。

ST 表最简单的应用就是查询区间最大值(或着最小值,这里以最大值为例),它(单纯 ST 表自己)需要你先修改之后(如果有修改要求),得到一个确切数组之后,经过 O ( nlogn ) 的预处理,然后就可以做到 O ( 1 ) 查询啦。


ST 表的预处理操作:

对于一个有 n 个数的 a [ n ] ,如果需要用一个二维数组 f [ n ] [ t ] ,其中 n 是指的用这 n 个数,t 是指的 n 最大是 2 的多少次幂,即 2 ^ t >= n 向上取整。

数组 f [ n ] [ t ] 是用来干什么的呢?f [ i ] [ j ] 是指以 a [ n ] 中第 i 个数开始,长度为 2 ^ j 的最大值,这样就清楚了数组 f [ n ] [ t ] 中 t 的来源啦吧。

所以我们要做的第一步预处理中:

f [ i ] [ 0 ] 就是存的 a [ i ] 值自己。

f [ i ] [ 1 ] 就是存的从 a [ i ] 开始往后一个,即 a [ i ] 和 a [ i + 1 ] 的最大值

……

以此类推,就可以得到我们想要的数组 f [ n ] [ t ] 啦。

那么我们怎么去实现这个东西呢?不能一个个的去枚举吧,那样的话,还不如用线段树(我是这么想得H_H),当然啦,这个问题在 ST 表被想出来的时候就解决啦,那就是递推得到,先看一下代码(不理解没关系,慢慢看)。

int f[maxn][20];
int a[maxn];
int n;
void st()
{
for(int i = 1; i <= n; i ++) f[i][0] = a[i];
int t = log(n) / log(2) + 1;
for(int j = 1; j < t; j ++)
{
for(int i = 1; i <= n - (1 << j) + 1; i ++)
{
f[i][j] = max(f[i][j-1],f[i + (1 << (j - 1))][j - 1]);
}
}
}

第一个 for 循环 应该没什么理解障碍,就是上面说的把自己存进去,因为是 2 ^ 0 。

第二个是两个 for 循环,也就是递推的部分,我们这样子来实现:

f [ i ] [ j ] 代表以 a [ i ] 开始长度为 2 ^ j 的数里面的最大值,即 f [ i ] [ j ] = max ( a[ i ] , …… , a [ i + 2 ^ j ] )。

我们先看一个简单的例子:现在一个数列是 a [ ]  = { 1 ,2,3,4,5} , 那么 (下标)0 ~ 2的最大值 Max1 ,和 3 ~ 4 最大值 Max2,两个里面的最大值 Max 就是整个区间的最大值,这个很好理解吧!

那么我们回到求 f [ i ] [ j ] 上,这个问题就变的和这个一样子啦,不过就是需要改变一下长度,我们把这个长度 len = 2 ^ j 的区间分成长度分别为 len1 = 2 ^ ( j - 1 ) 和 len2 =  2 ^ ( j - 1 ) 的这两个区间,只要求出来这两个的最大值就可以像上面那样子得到最终结果啦。

所以我们就可以理解上面我说的意思啦:f [ i ] [ j - 1] 代表从 a [ i ] 开始前 2 ^ ( j - 1 ) 个元素的最值,f [ i + ( 1 << ( j - 1 ) )] [ j - 1 ] 就是后面这 2 ^ ( j - 1 ) 个元素里面的最值,这样子合并取个最大值,就是总的最大啦,也就是代码中的:f [ i ] [ j ] = max ( f [ i ] [ j - 1 ] , f [ i + ( 1 << ( j - 1 ) ) ] [ j - 1 ] ) 。


这里有几点需要注意的(自己xj说的,不对可以告诉我哈):

第一点是大家都知道的,在预处理中控制第二维的循环,也就是 for ( int j = 1; j <= t; j ++) ,一般的话 t 取 20 左右就可以啦,你要是不放心就先计算一下再开数组一样的,这个一定要放在外面,因为我们要通过递推得到,如果放在里面的话,就得不到我们想要的结果啦(可以感觉一下子)。

第二点就是那个 f [ i ] [ j ] 这里可能有理解误区,就算是不够 2 的整数次幂也没有关系的,初始化或者边界处理一下就可以啦,最大值最小值对应上相应的初始化。

第三点是对于 1  << ( j  - 1 )  这个东西,这和求那个 t 的时候的意思一个样子啦,这样子写比较高大尚一些。

ST 表在预处理时采用倍增和DP思想。


ST 表查询操作:

关于查询操作,想一想怎么样子可以做到 O ( 1 ) 查询的呢。

先来看简单的栗子(简单的看懂啦,难得就可以啦):

a [ ]  = { 1,2,4,5,6,3,6,8,7,0 },在这些数里面我们想知道 1 ~ 8(下标从 0 开始) 的最大值。

如果我们已经用数组 f [ n ] [ t ] 存好啦,我们可以先计算一下长度可以是 2 的多少次幂,左端点是 x = 1, 右端点是 y = 8,长度 m = log ( y - x  + 1) / log ( 2 ),我们需要差的就是从 a [ x ] 开始 m 这么长的区间,由于 m 存在边界问题,所以我们还需要查的区间就变成 [ x ] [ m ] ,但是这样子查的话不免可能会漏掉东西而且预处理的结果我们也没有用到,所以我们把这个 m 的长的区间分成啦两个部分,也就可以利用预处理的结果啦。

所以区间就变成了 [ x ] [ x + 2 ^ m - 1 ] 和 [ y - 2 ^ m + 1] [ y ] ,这两个区间分别对应的数组 f [ n ] [ t ] 是 f [ x ] [ m ] 和 f [ y - ( 1 << m ) + 1 ] [ m ] (这个可能存在不是很好理解的问题,不过不要忘记啦 f [ i ] [ j ] 的意思,是指从 a [ i ] 开始的长度为 2 ^ j 的最值,这样就可以啦,实在不好理解,可以手动画一画)。

代码:

int query(int x, int y)
{
int t = log(abs(y-x + 1))/ log(2);
int a = f[x][t];
int b = f[y - (1 << t) + 1][t];
return max(a,b);
}

完整的代码(其实仅仅是一点点,可以戳我):

const int maxn = 1000004;
int f[maxn][20];
int a[maxn];
int n;
void st()
{
for(int i = 1; i <= n; i ++) f[i][0] = a[i];
int t = log(n) / log(2) + 1;
for(int j = 1; j < 20; j ++)
{
for(int i = 1; i <= n - (1 << j) + 1; i ++)
{
f[i][j] = max(f[i][j-1],f[i + (1 << (j - 1))][j - 1]);
}
}
}
int query(int x, int y)
{
int t = log(abs(y-x + 1))/ log(2);
int a = f[x][t];
int b = f[y - (1 << t) + 1][t];
return max(a,b);
}
---------------------
作者:Mercury_Lc

就罗嗦这么多啦,还有好多作业QAQ(看在我这么惨的份上,要是转载,别忘记啦我的地址QWQ)

ST表 「 从入门到入门 · 浅显理解 」的更多相关文章

  1. ST表入门学习poj3264 hdu5443 hdu5289 codeforces round #361 div2D

    ST算法介绍:[转自http://blog.csdn.net/insistgogo/article/details/9929103] 作用:ST算法是用来求解给定区间RMQ的最值,本文以最小值为例 方 ...

  2. 【基础算法-ST表】入门 -C++

    前言 学了树状数组看到ST表模板跃跃欲试的时候发现完全没思路,因为给出的查询的时间实在太短了!几乎是需要完成O(1)查询.所以ST表到底是什么神仙算法能够做到这么快的查询? ST表 ST表是一个用来解 ...

  3. st表树状数组入门题单

    预备知识 st表(Sparse Table) 主要用来解决区间最值问题(RMQ)以及维护区间的各种性质(比如维护一段区间的最大公约数). 树状数组 单点更新 数组前缀和的查询 拓展:原数组是差分数组时 ...

  4. 「ZJOI2018」胖(ST表+二分)

    「ZJOI2018」胖(ST表+二分) 不开 \(O_2\) 又没卡过去是种怎么体验... 这可能是 \(ZJOI2018\) 最简单的一题了...我都能 \(A\)... 首先我们发现这个奇怪的图每 ...

  5. 「学习笔记」ST表

    问题引入 先让我们看一个简单的问题,有N个元素,Q次操作,每次操作需要求出一段区间内的最大/小值. 这就是著名的RMQ问题. RMQ问题的解法有很多,如线段树.单调队列(某些情况下).ST表等.这里主 ...

  6. 「LuoguP3865」 【模板】ST表 (线段树

    题目背景 这是一道ST表经典题——静态区间最大值 请注意最大数据时限只有0.8s,数据强度不低,请务必保证你的每次查询复杂度为 O(1) 题目描述 给定一个长度为 N 的数列,和 M 次询问,求出每一 ...

  7. [模板]ST表浅析

    ST表,稀疏表,用于求解经典的RMQ问题.即区间最值问题. Problem: 给定n个数和q个询问,对于给定的每个询问有l,r,求区间[l,r]的最大值.. Solution: 主要思想是倍增和区间d ...

  8. SAP模块一句话入门(专业术语的理解)

    SAP模块一句话入门(专业术语的理解) SAP一句话入门:Financial & Controlling Accounting (FICO) 财务,财务,呵呵,让我们关心一下给我发工资的部门. ...

  9. [译]:Xamarin.Android开发入门——Hello,Android深入理解

    返回索引目录 原文链接:Hello, Android_DeepDive. 译文链接:Xamarin.Android开发入门--Hello,Android深入理解 本部分介绍利用Xamarin开发And ...

随机推荐

  1. 算术 HDU - 6715 (莫比乌斯反演)

    大意: 给定$n,m$, 求$\sum\limits_{i=1}^n\sum\limits_{j=1}^m\mu(lcm(i,j))$ 首先有$\mu(lcm(i,j))=\mu(i)\mu(j)\m ...

  2. SQL Sever 刪除重複數據只剩一條

    use book go create table ##T1( n int, a nvarchar(20) ) --查詢重複記錄,插入臨時表 insert into ##T1(n,a) select s ...

  3. awk 常用选项及数组的用法和模拟生产环境数据统计

    awk 常用选项总结 在 awk 中使用外部的环境变量 (-v) awk -v num2="$num1" -v var1="$var" 'BEGIN{print ...

  4. 关于get 和post 方法的比较

    地址:https://my.oschina.net/leejun2005/blog/136820 点击这里

  5. javascript_06-控制流程

    流程控制 程序的三种基本结构 顺序结构 选择结构 循环结构 判断语句 if 语法: if(condition){ //todo }else if{ //todo }else{ //todo } var ...

  6. Java【tomcat】配置文件

    Tomcat(二):tomcat配置文件server.xml详解和部署简介   分类: 网站架构   本文原创地址在博客园:https://www.cnblogs.com/f-ck-need-u/p/ ...

  7. KubeEdge,一个Kubernetes原生边缘计算框架

    ​KubeEdge成为第一个Kubernetes原生边缘计算平台,Edge和云组件现已开源! 开源边缘计算正在经历其业界最具活力的发展阶段.如此多的开源平台,如此多的整合以及如此多的标准化举措!这显示 ...

  8. 链表(python)

    链表1.为什么需要链表顺序表的构建需要预先知道数据大小来申请连续的存储空间,而在进行扩充时又需要进行数据的搬迁,所以使用起来并不是很灵活.链表结构可以充分利用计算机内存空间,实现灵活的内存动态管理. ...

  9. Git 的用法

    对于GIT  的用法,最近一直在寻找方法.网上也能找到一些方法.但是感觉说的不是很清楚,在这里我基于自己经验写一些. 对于任何一种方法都要安装GIT. 我是基于VS Code 2015 来做的. 在安 ...

  10. 2019-2020-1 20199301《Linux内核原理与分析》第三周作业

    操作系统是如何工作的 本章目标是在mykernel的基础上编写一个简单的内核 一.学习笔记 1.计算机的三个法宝:a.存储程序计算机:b.函数调用堆栈:c.中断 存储程序计算机(所有计算机的基础性的逻 ...