首页 > 其他分享 >ST 表学习笔记

ST 表学习笔记

时间:2022-08-26 02:05:00浏览次数:77  
标签:log minn int 代码 笔记 ST 学习 dp

目录

  1. 什么是 ST 表

  2. 建造 ST 表

  3. ST 表查询操作

  4. ST 表的适用范围与数据结构的对比

  5. 模板题

  6. 结语&参考文献

什么是 ST 表

ST 表,即 Sprase Table,也叫稀疏表。
它主要用于解决 RMQ(Range Minimum/Maximum Query,区间最值查询)问题。

更生动地讲,ST 表就是在 \(O(n \log n)\) 的时间里预处理个表,然后可以在 \(O(1)\) 的时间内查询一个区间的最大值或最小值。

ST 表本质上是动态规划

建造 ST 表

与线段树、树状数组一样,ST 表要先建造。

假设我们需要求区间最小值。

设 \(dp_{i, j}\) 表示以 \(i\) 为头,长度为 \(2^j\) 的区间的最小值。

例如,\(dp_{5, 2}\) 表示 \([5, 8]\) 的元素的最小值。

int dp[数组大小 N][log(N)+5];

如 \(N = 10^5\),就可以定义成: int minn[100005][20]


首先初始化,令 \(dp_{i, 0} = a_i\) 即可。

for (int i = 1; i <= n; i++) minn[i][0] = read();

* 程序中为了区分是求最大值还是最小值,将 dp 改成了 minn。不过终究是个名字罢了,不用在意。

状态转移方程很简单:\(dp_{i, j} = \min(dp[i][j-1], dp[i+2^{j-1}][j-1])\)。

原因更简单,长度为 \(2^j\) 的最值就是两段长度为 \(2^{j-1}\) 合起来的最值。

外层 for 循环:先枚举长度 \(len\),即 \(2^1\) 到 \(2^{\left\lfloor\log n\right\rfloor}\)。

内层 for 循环:枚举起点,即 \(1\) 到 \((n - 2^{len} + 1)\)。

代码:

void build()
{
    for (int i = 1; i <= n; i++) minn[i][0] = read();
    int k = log2(n);
    for (int j = 1; j <= k; j++)
    {
        int R = n - (1 << j) + 1;
        for (int i = 1; i <= R; i++) minn[i][j] = min(minn[i][j-1], minn[i + (1 << (j-1))][j-1]);
    }
}

* 代码中的 log2()cmath 库中的函数。

ST 表查询操作

前面已经提到,给定 \(L\) 与 \(R\),求 \(\min\limits_{R}^{i=L} a_i\) 的值。

设 \(k = \left\lfloor\log(R-L+1)\right\rfloor\),其中 \((R-L+1)\) 为区间长度。

这张图很清晰地讲解了 ST 表的查询操作。

那么代码实现就非常容易了。

int query(int L, int R)
{
	int k = log2(R-L+1);
	return min(minn[L][k], minn[R - (1<<k) + 1][k]);
}

ST 表的适用范围与数据结构的对比

在这章开始之前,先扔个完整 ST 表代码:

//结构体外的定义
#define N 100005
int n;
//结构体
struct ST
{
    int minn[N][20];
	void build()
	{
	    for (int i = 1; i <= n; i++) minn[i][0] = read();
	    int k = log2(n);
	    for (int j = 1; j <= k; j++)
	    {
	        int R = n - (1 << j) + 1;
	        for (int i = 1; i <= R; i++) minn[i][j] = min(minn[i][j-1], minn[i + (1 << (j-1))][j-1]);
	    }
	}
	int query(int L, int R)
	{
		int k = log2(R-L+1);
		return min(minn[L][k], minn[R - (1<<k) + 1][k]);
	}
};
ST a;

用结构体封装了,代码很清新吧!

大家观察一下代码,不难发现:

  • build() 的时间复杂度是 \(O(n \log n)\)。

  • query() 的时间复杂度是 \(O(1)\)。

我们发现,线段树同样可以解决这类问题。

将线段树拉过来对比一下:

* 前台图炸了,只能在后台截图QwQ

通过表格可以看到,两者各有利弊。

模板题

T1:P1816

求最小。

直接用上文的代码即可。

T2:P3865

求最大。

代码稍微更改一下就好。

T3:P2880

同时维护最大与最小的 ST 表。

查询时作差即可,非常简单。

代码略。

* 如果你真的想学习 ST 表,以上模板题建议从零开始盲打一遍。

结语 & 参考文献

感谢大家看完整篇文章。

大部分内容参考了书籍《算法训练营:进阶篇》。

图片均为自己手绘,图片与文章未经允许不得转载。

首发:2022-05-07 16:35:56

标签:log,minn,int,代码,笔记,ST,学习,dp
From: https://www.cnblogs.com/liangbowen/p/16622859.html

相关文章

  • @DataJpaTest 进行测试的坑
    @DataJpaTest 这个注解主要用来在Spring项目中测试JPA数据源。默认情况下,带有 @DataJpaTest 注解的测试使用嵌入式内存数据库。因此 @DataJpaTest 这个注解还是......
  • 快速幂学习笔记
    前言快速幂很有用哦!!目前本文还没有例题,因为没有什么好题啊。以后看一下能不能找一些题目。什么是快速幂幂,也就是次幂,可以理解为计算\(x^y\)。由于\(x^y\)会特别......
  • 状态压缩 DP 学习笔记【入门篇】
    前言状态压缩DP,简称状压DP。之前一直觉得状压特别难,学了一下,发现基本形态挺简单的。在学习之前,你需要掌握:简单DP(如线性DP,背包)基本二进制运算:&运算、|运算、\(......
  • 拓扑排序学习笔记
    前言在学习这篇文章之前,你需要了解的算法有:基本图论知识链式前向星(图的一种存储方式)了解队列、栈等简单数据结构的实现,用STL也行。什么是拓扑排序AOV网的定义......
  • java学习:八大基本类型变量
    1.类在java中用class来定义一个类,类是java程序的基本单位类描述的是具有共性的一类事物,所以我们又可以把类称作为模板技术 如何理解共性:具有相同的属性--》j......
  • 【ElasticSearch】索引生命周期管理(三) 避坑指南
    背景主要是针对在使用索引生命周期的去管理索引的过程中,记录所踩到坑,避免同样的问题再次发生问题1. 索引生命周期中设置各个阶段的市场以及索引rollover的时间......
  • Java -> Stream入门
    学习Stream的目的函数式编程渐渐变成主流,为了看懂同事的代码。相对于传统的编程方式,代码更为简洁清晰易懂。使得并发编程变得如此简单。有效的避免了代码嵌套......
  • 05.爬虫入门笔记1
    入门爬虫笔记011.request库的使用使用request库的get方法importrequestr=request.get('www.baidu.com')这会得到一个Response对象,将其存入变量r。显示得到的......
  • 基于 .NET 6 的轻量级 Webapi 框架 FastEndpoints
    大家好,我是等天黑。FastEndpoints 是一个基于.NET6开发的开源webapi框架,它可以很好地替代.NETMinimalAPIs和MVC,专门为开发效率而生,带来了全新的开发模式和编......
  • SSCMS文件解析-学习笔记
    //声明常量,不可变constfs=require('fs-extra');//初始化目录插件constdel=require('del');//删除文件的工具constgulp=require('gulp');//基于流的代码自动化......