当前位置: 首页 > news >正文

【CSP-S】数据结构 ST 表详解

作者要备战 Csp-s 比赛,大家可以查看相关的专栏 传送门

ST表可以干什么

ST 表主要用于求解静态区间 RMQ 问题,静态区间 RMQ 问题就是给你一个数组,然后给你几个区间,计算区间和,区间最大值,区间最小值等问题

ST 表的优势

ST 表可以快速查询区间 RMQ 问题。需要一个 O(nlog⁡n)O(n \log n)O(nlogn) 的预处理,但是单次查询只要 O(1)O(1)O(1) ,对于线段树和树状数组这两个数据结构的查询 O(log⁡n)O(\log n)O(logn) 他会更快!

其次 ST 表编程简单,代码量相对于 线段树和树状数组简单 而且常数更小

ST 表的劣势

ST 表只可以求解静态 RMQ 问题,不支持修改操作。而且 ST 表只支持可重复性运算(这个后面会讲到)

ST 表的实现原理

Step1: 预处理

ST 表主要是基于倍增的思想进行预处理。

定义

我们设 dp[start][size]dp[start][size]dp[start][size] 表示从 startstartstart 开始,
区间长度为 2size2^{size}2size 的答案。

实现

那么我们怎么处理这个 dpdpdp 数组呢?我们考虑使用倍增的方法实现
公式
dp[start][size]=dp[start][size−1]+dp[start+2size−1][size−1]dp[start][size] = dp[start][size - 1] + dp[start + 2^{size-1}][size-1]dp[start][size]=dp[start][size1]+dp[start+2size1][size1]

下面进行一些推导
当前的区间 dp[start][size]dp[start][size]dp[start][size] 可以看成两个区间的合并而成。


第一个区间dp[start][size−1]dp[start][size-1]dp[start][size1]
startstartstart 位置开始,长度为 2size−12^{size-1}2size1 的区间


第二个区间: dp[start+2size−1][size−1]dp[start + 2^{size - 1}][size-1]dp[start+2size1][size1]
start+2size−1start + 2^{size - 1}start+2size1 开始,长度为 2size−12^{size-1}2size1


其实很简单的一个道理
长度为 2size2^{size}2size 的区间一定会由两个长度为 2size−12^{size-1}2size1 的区间合并得到。

因为 2size−1+2size−1=2size2^{size-1} + 2^{size-1} = 2^{size}2size1+2size1=2size
如果还是不懂,我们可以证明一下 2x−1+2x−1=2x2^{x-1} + 2^{x-1} = 2^{x}2x1+2x1=2x
证明:
左边=2x−1+2x−1=2x−1×2=2x左边 = 2^{x-1} + 2^{x-1} = 2^{x-1} \times 2 = 2^x左边=2x1+2x1=2x1×2=2x

只是将第二个区间起点的位置变化成了第一个区间起点 + 第一个区间长度而已。

代码
for (int size = 1; size <= Log2[n]; size++) // Log2[i] 表示 log2(i) 是多少for (int start = 1; start + (1 << size) <= n; start++) // 枚举开始位置ST[start][size] = max(ST[start][size - 1], ST[start + (1 << (size - 1))][size - 1]);

Step2: 查询

这里我们要考虑如何查询。
首先我们发现一个观点,任何一个区间都可以化成两个长度为 2i2^i2i 的区间相加

比如区间 [5,11][5, 11][5,11] 可以化为 [5,8][5, 8][5,8](长度为 444222^222) 和 [8,11][8, 11][8,11](长度为 444222^222)

但是我们会发现,这里面会有一些元素计算过两次!比如 888,所以 ST 表只可以计算 区间最大值,区间最小值 因为一个数就算比较很多遍不影响最大最小值 但是 ST 表不可以计算区间和 因为会有元素计算了 222

所以我们可以正着查询一次,然后倒着查询一次即可

什么意思?
就是指我们查询当前的左边界可以延伸到的最长位置(不超过右边界)
然后我们再次查询当前的右边界可以向左延伸的最长位置(不超过左边界)
两个答案相加即为最终的答案

注意区间是从左到右的,在进行步骤 2 时要减去区间长度

代码
int s = Log2[r - l + 1];
printf("%d\n", max(ST[l][s], ST[r - (1 << s) + 1][s]));

例题 区间最大值问题

题目位置:https://www.luogu.com.cn/problem/P3865
代码:

#include <stdio.h>
#include <ctype.h>//#define getchar getchar
//int isdigit(char x) {return ((x) >= '0' && (x) <= '9');}int read() {int ret = 0;char ch;while (!isdigit(ch = getchar()));do {ret = ret * 10 + ch - '0';} while (isdigit(ch = getchar()));return ret;
}#define N ((int) 1e5 + 10)
int Log2[N];
int n, m;void InitLog2(void)
{Log2[1] = 0; // 2^0 = 1for (int i = 2; i <= n + 1; i++) Log2[i] = Log2[i / 2] + 1;
}
int max(int a, int b) {return a > b ? a : b;}int ST[N][18];
int arr[N];void InitSt(void)
{for (int i = 1; i <= n; i++) ST[i][0] = arr[i]; // 区间长度为 1for (int size = 1; size <= Log2[n]; size++)for (int start = 1; start + (1 << size) <= n; start++) // 枚举开始位置ST[start][size] = max(ST[start][size - 1], ST[start + (1 << (size - 1))][size - 1]);
}int main()
{n = read(), m = read();
//    n = read();for (int i = 1; i <= n; i ++) arr[i] = read();InitLog2(), InitSt();while (m--) {int l = read(), r = read();int s = Log2[r - l + 1];printf("%d\n", max(ST[l][s], ST[r - (1 << s) + 1][s]));}return 0;
} 

文章转载自:

http://gzZPsHoL.gmysq.cn
http://z8c669GY.gmysq.cn
http://p9CNkJgj.gmysq.cn
http://ajegfVRh.gmysq.cn
http://we3FvGDq.gmysq.cn
http://2mjFfykT.gmysq.cn
http://zZOqSslT.gmysq.cn
http://x6JgEZf8.gmysq.cn
http://dFvJ2hvK.gmysq.cn
http://h0vNV3Ca.gmysq.cn
http://RFID4yuP.gmysq.cn
http://ZUrvsTnj.gmysq.cn
http://jgaozGIs.gmysq.cn
http://C0SBn4U8.gmysq.cn
http://KzNSXjGT.gmysq.cn
http://tIj4uwCo.gmysq.cn
http://B3LjAStd.gmysq.cn
http://YKzbjkXX.gmysq.cn
http://zsKYGlzb.gmysq.cn
http://vA26trYb.gmysq.cn
http://wLyg7RFw.gmysq.cn
http://JBIW7Q3H.gmysq.cn
http://AP9wi2Hd.gmysq.cn
http://WWRKqu7f.gmysq.cn
http://HdcKmo0Q.gmysq.cn
http://s96cGF3Q.gmysq.cn
http://HnOMd1Qs.gmysq.cn
http://NyfZze1B.gmysq.cn
http://jQuDTQ40.gmysq.cn
http://pto2YpTm.gmysq.cn
http://www.dtcms.com/a/370910.html

相关文章:

  • 积分球的使用——简易版
  • 基于Echarts+HTML5可视化数据大屏展示-图书馆大屏看板
  • 讲解一下冒泡排序
  • 【基础-单选】关于容器组件Row和Column,下面说法错误的是
  • 【佳易王药品进销存软件实测】:操作简单 + 全流程管理,医药台账管理好帮手#软件教程全解析
  • 项目升级--Nginx
  • HOT100--Day13--104. 二叉树的最大深度,226. 翻转二叉树,101. 对称二叉树
  • 修复存在坏块或05、C4、C5 S.M.A.R.T错误的硬盘
  • LwIP入门实战 — 4 LwIP的网络接口管理
  • rapidocr v3.4.0发布了
  • 硬件开发_基于物联网的老人跌倒监测报警系统
  • ARM体系结构学习①
  • 【LeetCode热题100道笔记】验证二叉搜索树
  • 垃圾收集器分类
  • AQS原理
  • Nestjs框架: 使用 CASL 库实现基于角色的权限控制(RBAC)与细粒度访问控制的实战演示
  • 计算机主板上的那颗纽扣电池的作用是什么?
  • 【Java实战㉗】Java日志框架实战:Logback与Log4j2的深度探索
  • 【关于线程的一些总结】
  • PyQt5 入门(上):开启 GUI 编程之旅
  • 本体论中的公理与规则——从经典逻辑到神经符号融合的演进
  • linux 内核 - 内核设计原则
  • Vue3中SCSS的使用指南
  • 音转文模型对比FunASR与Faster_whisper
  • 【YOLOv11】3.Pycharm配置
  • 常用配置文件
  • MySQL运维补充
  • JVM中如何调优新生代和老生代?
  • Transformer 架构的演进与未来方向(RNN → Self-Attention → Mamba)——李宏毅大模型2025第四讲笔记
  • 企业级监控方案对比:Zabbix vs Prometheus