首页 > 编程语言 >[算法学习笔记] ST表

[算法学习笔记] ST表

时间:2023-10-15 22:56:34浏览次数:34  
标签:ch log int 笔记 ST 算法 RMQ 区间

学习时间:2023/10/15

CSP-S 2023 倒计时 5 days 我竟然才会ST表

简述

ST表主要用于解决 静态RMQ问题。实际上,凡是具备 可重复贡献和结合律的问题,都可以用 ST表 来解决。

ST表 的优化方式和前缀和差分类似,采取预处理,每次可以做到 \(O(1)\) 时间复杂度的查询。因此,它适用于 有大量查询操作的问题。如果牵扯到大量修改需要使用线段树或者分块等其他算法。(这就是称 ST表 适用于 静态RMQ问题而不是动态 RMQ 问题的原因)

对于静态 RMQ 问题,ST表的时间复杂度比线段树更优,而且更加好写,不容易犯错。

因此,在解决 RMQ 问题时,我们需要根据题意来选择 ST表或者线段树。

实现原理

ST表 运用了倍增的思想,实现基于动态规划,具体地,定义 \(f_{i,j}\) 表示从 \(i\) 开始连续 \(2^j\) 个数中的最大值。

初始预处理,我们可以采用动态规划的方法, 把一个区间分成两部分,第一个部分为 \((i,j-1)\),第二个部分为 \((1>>(j-1),j-1)\)。原理如图所示。
image

(图片选自Pecco的文章,这里表示感谢。)

对于查询,查询区间 \((l,r)\) 中,我们将其分为两部分,分别为:

\((l,l+2^s-1),(r-2^s+1,r)\)

我们显然期望符合条件的 \(s\) 最大,考虑最优情况,当 \(l+2^s-1=r\) 时,移项:

\[2^s=r+1-l \]

即:

\[s=\log_{2}(r-l+1) \]

当然 \(s\) 必须是整数,我们需要向下取整,也就是:

\[s=\lfloor\log_{2}(r-l+1)\rfloor \]

这是左区间的 right,也就是 \(2\) 的整次幂部分,对于右区间,查找区间 \((r-(2^s)+1,r)\) 即可。

由于我们只是需求它们两个区间的最大值,这两个区间可以重复,也就是满足上面所提到的 可重复贡献,反之,对于求区间和问题不可以使用该方法。

小技巧:对于 \(log\) 的求解,C++ 自带的log2函数太慢了,我们可以通过递推预处理。

递推式非常显然:\(log_i=log_{i\div 2}+1\)

如图:

image
(图片选自Pecco的文章,这里表示感谢。)

可以证明这两个区间可以覆盖区间 \(l,r\)。

具体地,对于每次查询,输出 \(max(f_{l,s},f_{r-(1>>s)+1,s})\) 即可。

模板题 & 参考代码

模板题评测地址:https://www.luogu.com.cn/problem/P3865

给定一个长度为 \(n\) 的数组 \(a\),共有 \(m\) 次询问,每次询问给定一个区间 \((l,r)\) ,求这个区间的最大值。

Analysis

发现本题没有修改操作,只是对数组初始值进行操作,是 静态RMQ 问题,考虑 ST表。

具体见代码:

参考代码
#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;
const int N = 100010;
int f[N][21];
int Log2[N];
int n,m;
inline int read()
{
	int x=0,f=1;char ch=getchar();
	while (ch<'0'||ch>'9'){if (ch=='-') f=-1;ch=getchar();}
	while (ch>='0'&&ch<='9'){x=x*10+ch-48;ch=getchar();}
	return x*f;
}
int main()
{
    cin>>n>>m;
    for(int i=1;i<=n;i++)
    {
        f[i][0] = read();
    }
    for(int i=1;i<=20;i++)
    {
        for(int j=1;j+(1<<i)-1 <= n;j++)
        {
            f[j][i] = max(f[j][i-1],f[j+(1<<(i-1))][i-1]);
        }
    }
    for(int i=2;i<=n;i++)
    {
        Log2[i] = Log2[i/2] + 1;
    }
    for(int i=0;i<m;i++)
    {
        int l , r;
        l = read();
        r = read();
        int s = Log2[r-l+1];
        cout<<max(f[l][s],f[r-(1<<s)+1][s])<<"\n";
    }
    return 0;
}

标签:ch,log,int,笔记,ST,算法,RMQ,区间
From: https://www.cnblogs.com/SXqwq/p/17766392.html

相关文章

  • 《用户故事与敏捷方法》阅读笔记(二)
      接下来的几章就是优秀用户故事准则、估算用户故事、发布计划、迭代计划测量并监控速率、故事不是什么、故事的优势以及故事的不良征兆。主要将的就是在一个大型项目中,尤其是有许多用户角色的项目,确定用户故事有时让人无从下手。最好的办法是考虑每一个角色,了解用户使用我们软......
  • 代码大全阅读笔记02
    1、以解决问题为导向不仅仅是要完成一个任务;一切的一切都以实际的问题和需求为导向,最终的目的只有一个,而不是一直变换目标,就是解决真正的问题;2、把程序员当人看我们在项目中要记得,这是一个项目团队,团队由不同的个体组成,总是需要磨合的,所以,这就需要我们不仅仅将成员当人看,也要......
  • 《信息安全专业导论》第六周学习笔记
      知识点总结:十一章EXT2系统EX2文件系统数据结构创建虚拟硬盘mke2fs[-bblksize-Nninodes]devicenblocks虚拟磁盘布局Block#0:引导块超级块Block#1容纳整个文件系统的信息超级块的重要字段:u32s_inodes_count://文件系统中节点总数u32s_blocks_count://文件系......
  • * Codeforces Round 665 (Div. 2) A. Distance and Axis
    有一个点\(A\)在\(OX\)正坐标轴上的\(x\)坐标为\(n\)。需要找到一个点\(B\),使得\(||OB|-|AB||=k\)。现在给出非负整数\(n\)\(k\),你可以执行任意次以下操作:每步操作可以使\(A\)的坐标加一或减一。询问最少需要进过多少次操作使\(B\)可以存在。先假设出......
  • Git笔记
    Git打标签gittagtagName-m"info"#打一个标签gittag-dtagName#删除一个标签gitshow#全部tag信息gitshowtagName#查看一个taggittag#全部tag省略信息回滚gitreset--hard"提交HASH值"#回退到指定版本gitpush-foriginmaster#强制提交......
  • Security Reduction学习笔记(1):密码系统与安全模型的定义
    课件地址:Book(uow.edu.au),原作者声明该课件对人类和外星人免费开放( ̄_ ̄||)现代密码学概念:现代密码学与经典密码学的区别在于它强调定义(definitions)、模型(models)和证明(proofs).定义澄清:密码学(Cryptology)=设计密码学(Cryptography)+分析密码学(Cryptanalysis)密码......
  • 学习笔记5
    关于知识点知识点归纳第十一章EXT2文件系统11.1EXT2文件系统EXT2(第二扩展文件系统)是一种用于Linux中的文件系统。文件系统结构:EXT2文件系统使用了多级的索引结构来组织文件和目录。它包括了超级块、inode、数据块、组描述符等数据结构。文件系统特性:EXT2文件系统支持文......
  • 学习笔记五
    EXT2文件系统EXT2文件系统多年来,Linux一直使用EXT2作为默认文件系统。EXT3相对于2,主要增加了一个日志文件;EXT4相对于3,主要是磁盘块的分配。 EXT2文件系统数据结构mkfs创建虚拟磁盘mke2fs[-bblksize-Nninodes]devicenblocks创建了一个带有nblocks个块(每个块大小blk......
  • C++学习笔记Day1
    有关const的一些事1.const对象必须初始化,因为const对象一旦创建,其值不能再被改变。2.const对象是常量,因此可以赋予其字面值。3.普通变量默认支持多文件下共享,而const默认不支持,需要在定义和声明是都加上关键字extern才能在多个文件中使用。4.所谓“常量引用”指的是“对const......
  • 算法修养--广度优先搜索BFS
    广度优先算法(BFS)广度优先算法(Breadth-FirstSearch)是在图和树领域的搜索方法,其核心思想是从一个起始点开始,访问其所有的临近节点,然后再按照相同的方式访问这些临近节点的节点,这种访问方式类似涟漪泛起,一层一层的扩散。广度优先算法解决的问题:从A点出发,有没有一条路径可以到达B......