每周一算法:倍增法求区间最大最小值(RMQ)

这篇具有很好参考价值的文章主要介绍了每周一算法:倍增法求区间最大最小值(RMQ)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

RMQ

RMQ 是英文 Range Maximum/Minimum Query 的缩写,表示区间最大(最小)值。使用倍增思想解决 RMQ 问题的方法是 ST 表(Sparse Table, 稀疏表 )。ST 表是用于解决 可重复贡献问题 的数据结构。

可重复贡献问题 是指对于运算 opt ⁡ \operatorname{opt} opt,满足 x opt ⁡ x = x x\operatorname{opt} x=x xoptx=x,则对应的区间询问就是一个可重复贡献问题。例如,最大值有 max ⁡ ( x , x ) = x \max(x,x)=x max(x,x)=x g c d gcd gcd gcd ⁡ ( x , x ) = x \operatorname{gcd}(x,x)=x gcd(x,x)=x,所以 RMQ 和区间 GCD 就是一个可重复贡献问题。像区间和就不具有这个性质,如果求区间和的时候采用的预处理区间重叠了,则会导致重叠部分被计算两次。另外, opt ⁡ \operatorname{opt} opt 还必须满足结合律才能使用 ST 表求解。

题目链接

题目链接:【模板】ST 表

题目描述

这是一道 ST 表经典题——静态区间最大值

请注意最大数据时限只有 0.8s,数据强度不低,请务必保证你的每次查询复杂度为 O ( 1 ) O(1) O(1)。若使用更高时间复杂度算法不保证能通过。

如果您认为您的代码时间复杂度正确但是 TLE,可以尝试使用快速读入:

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

函数返回值为读入的第一个整数。

快速读入作用仅为加快读入,并非强制使用。

题目描述

给定一个长度为 N N N 的数列,和 M M M 次询问,求出每一次询问的区间内数字的最大值。

输入格式

第一行包含两个整数 N , M N,M N,M,分别表示数列的长度和询问的个数。

第二行包含 N N N 个整数(记为 a i a_i ai),依次表示数列的第 i i i 项。

接下来 M M M 行,每行包含两个整数 l i , r i l_i,r_i li,ri,表示查询的区间为 [ l i , r i ] [l_i,r_i] [li,ri]

输出格式

输出包含 M M M 行,每行一个整数,依次表示每一次询问的结果。

样例 #1

样例输入 #1

8 8
9 3 1 7 5 6 0 8
1 6
1 5
2 7
2 6
1 8
4 8
3 7
1 8

样例输出 #1

9
9
7
7
9
8
7
9

提示

对于 30 % 30\% 30% 的数据,满足 1 ≤ N , M ≤ 10 1\le N,M\le 10 1N,M10

对于 70 % 70\% 70% 的数据,满足 1 ≤ N , M ≤ 10 5 1\le N,M\le {10}^5 1N,M105

对于 100 % 100\% 100% 的数据,满足 1 ≤ N ≤ 10 5 1\le N\le {10}^5 1N105 1 ≤ M ≤ 2 × 10 6 1\le M\le 2\times{10}^6 1M2×106 a i ∈ [ 0 , 10 9 ] a_i\in[0,{10}^9] ai[0,109] 1 ≤ l i ≤ r i ≤ N 1\le l_i\le r_i\le N 1liriN

算法思想

ST 表基于倍增思想,可以做到 O ( n log ⁡ n ) O(n\log n) O(nlogn) 预处理, O ( 1 ) O(1) O(1) 回答每个询问。但是不支持修改操作

基于倍增思想,考虑如何求出区间最大值。可以发现,如果按照一般的倍增流程,每次跳 2 i 2^i 2i步的话,询问时的复杂度仍旧是 O ( log ⁡ n ) O(\log n) O(logn),效率较低。

由于区间最大值是一个具有可重复贡献性质的问题。哪怕用来求解的预处理区间有重叠部分,只要这些区间合并是所求的区间,最终计算出的答案就是正确的。举个例子:

每周一算法:倍增法求区间最大最小值(RMQ),每周一算法,算法,青少年编程,信息学竞赛,c++

区间 [ 2 , 5 ] [2,5] [2,5]的最大值为 5 5 5,区间 [ 4 , 7 ] [4,7] [4,7]的最大值为 7 7 7,区间 [ 2 , 7 ] [2,7] [2,7]的最大值为 max ⁡ { 5 , 7 } = 7 \max\{5,7\}=7 max{5,7}=7

通过ST表,使用至多两个预处理过的区间就可以覆盖询问区间,也就是说询问时的时间复杂度可以被降至 O ( 1 ) O(1) O(1),在处理有大量询问的题目时十分有效。

预处理ST表

状态表示

  • f [ i ] [ j ] f[i][j] f[i][j]表示区间 [ i , i + 2 j − 1 ] [i,i+2^j-1] [i,i+2j1]的最大值。

状态计算

要计算区间 [ i , i + 2 j − 1 ] [i,i+2^j-1] [i,i+2j1]的最大值,区间大小为 2 j 2^j 2j,相当于从位置 i i i跳了 2 j − 1 2^j-1 2j1,依据倍增的思想,可以将整个区间一分为二,左侧区间 [ i , i + 2 j − 1 − 1 ] [i,i+2^{j-1}-1] [i,i+2j11],右侧区间 [ i + 2 j − 1 , i + 2 j − 1 ] [i+2^{j-1},i+2^j-1] [i+2j1,i+2j1],大小均为 2 j − 1 2^{j-1} 2j1,如下图所示:
每周一算法:倍增法求区间最大最小值(RMQ),每周一算法,算法,青少年编程,信息学竞赛,c++
那么状态转移方程:

f [ i ] [ j ] = m a x { f [ i ] [ j − 1 ] , f [ i + 2 j − 1 ] [ j − 1 ] } f[i][j]=max\{f[i][j-1],f[i+2^{j-1}][j-1]\} f[i][j]=max{f[i][j1],f[i+2j1][j1]}

初始状态

  • f [ i ] [ 0 ] = a i f[i][0]=a_i f[i][0]=ai

查询区间最值

对于每个询问 [ L , R ] [L,R] [L,R],把它成两个部分 [ L , L + 2 k − 1 ] [L,L+2^k-1] [L,L+2k1] [ R − 2 k + 1 , R ] [R-2^k+1,R] [R2k+1,R],其中 k = ⌊ l o g 2 ( R − L + 1 ) ⌋ k=\lfloor log_2(R-L+1)\rfloor k=log2(RL+1)⌋,两部分的最值就是答案。文章来源地址https://www.toymoban.com/news/detail-782759.html

时间复杂度

  • 预处理 ST 表的时间复杂度为 O ( n log ⁡ n ) O(n\log n) O(nlogn)
  • 回答每个询问的时间复杂度 O ( 1 ) O(1) O(1)

代码实现

#include <iostream>
#include <cmath>
using namespace std;
const int N = 1e5 + 10, M = 20;
int n, a[N], f[N][M];
//创建ST表
void create() {
    //初始状态
    //f[i][0]表示从i开始长度为2^0的区间最值为a[i]本身
    for(int i = 1; i <= n; i ++) f[i][0] = a[i];
    int k = log2(n);
    //枚举区间长度指数j
    for(int j = 1; j <= k; j ++)
        for(int i = 1; i + (1 << j) - 1 <= n; i ++)
            f[i][j] = max(f[i][j - 1], f[i + (1 << j - 1)][j - 1]);
}
//利用ST表查询区间[L,R]的最大值
int query(int L, int R) {
    int k = log2(R - L + 1);
    return max(f[L][k], f[R - (1 << k) + 1][k]);
}
int main()
{
    int m;
    cin >> n >> m;
    for(int i = 1; i <= n; i ++) scanf("%d", a + i);;
    create();
    while(m --) {
        int L, R;
        scanf("%d%d", &L, &R);
        printf("%d\n", query(L, R));
    }
}

到了这里,关于每周一算法:倍增法求区间最大最小值(RMQ)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • [每周一更]-(第82期):认识自然处理语言(NLP)

    GPT的大火,带起了行业内大模型的爆发;国内外都开始拥有或者研发自己的大模型,下边我们从NLP来进一步深入了解大模型、AI。 一、什么是NLP? 自然语言处理 (英语:Natural Language Processing,缩写作 NLP )是人工智能和语言学领域的分支学科。此领域探讨如何处理及运用自然

    2024年01月16日
    浏览(43)
  • [每周一更]-(第69期):特殊及面试的GIT问题解析

    整合代码使用过程的问题,以及面试遇到的细节,汇总一些常用命令的对比解释和对比; 1、fetch和pull区别 git fetch是将远程主机的最新内容拉到本地,用户在检查了以后决定是否合并到工作本机分支中。 git pull则是将远程主机的最新内容拉下来后直接合并,即:git pull = git

    2024年02月08日
    浏览(43)
  • [每周一更]-(第54期):Go的多版本管理工具

    参考 https://zhuanlan.zhihu.com/p/611253641 https://learnku.com/articles/78326 前文概要 Go语言从开始使用从1.13起步,随着泛型的支持,带领团队在转型Go的时候,做基础组件架构选型使用1.18,但是Go版本不断迭代想使用最新版本来体验下,类比前端中node,有个nvm工具; 联想到Go应该也会有对

    2024年02月16日
    浏览(38)
  • [每周一更]-(第27期):HTTP压测工具之wrk

    [补充完善往期内容] wrk是一款简单的HTTP压测工具,托管在Github上,https://github.com/wg/wrk wrk 的一个很好的特性就是能用很少的线程压出很大的并发量. 原因是它使用了一些操作系统特定的高性能 io 机制, 比如 select, epoll, kqueue 等. 其实它是复用了 redis 的 ae 异步事件驱动框架. 确切的

    2024年02月03日
    浏览(38)
  • [每周一更]-(第45期):Docker私有镜像仓库配置并打通阿里云OSS

    Docker Registry 2 官方镜像创建一个私有镜像仓库,将Docker 镜像上传到 OSS 相应的路径中。 参考: BatchCompute Docker支持:https://help.aliyun.com/document_detail/143334.html?spm=a2c4g.143333.0.0.4a6f8752ls18FR Docker Registry:https://docs.docker.com/registry 基于OSS搭建私有 Docker Registry:https://developer.aliyun.com

    2024年02月03日
    浏览(43)
  • [每周一更]-(第83期):Go新项目-Gin中间件的使用和案例(10)

    在 Gin 中,中间件是一种用于处理 HTTP 请求和响应的功能强大的机制。中间件是一段位于请求处理链和最终处理器之间的代码, 它可以截获请求、执行预处理操作,修改请求或响应,然后将控制权传递给下一个中间件或最终的请求处理器。 中间件在业务使用中,方便注入一些

    2024年01月20日
    浏览(54)
  • [每周一更]-(第57期):用Docker、Docker-compose部署一个完整的前后端go+vue分离项目

    将公司物理机项目改为容器化部署,最终方案是通过容器docker-compose部署使项目可以ip+端口访问,再通过物理机nginx代理进行https域名访问。 可能还有更好的方式,开一个nginx的容器,进行代理,但由于跟物理机80,443端口冲突,暂时没有采用。 可能目前处理不是最好的方式,不

    2024年02月14日
    浏览(41)
  • 更快的求最近公共祖先(LCA)的算法-倍增法求LCA

    236. 二叉树的最近公共祖先 参考:最近公共祖先 f a [ i ] [ j ] rm{fa}[i][j] fa [ i ] [ j ] 表示结点 i i i 的第 2 i 2^i 2 i 个组先 d e p [ i ] rm{dep}[i] dep [ i ] 表示结点 i i i 的深度 n e x t [ i ] [ 0 ] rm{next}[i][0] next [ i ] [ 0 ] 表示结点 i i i 的左子结点 n e x t [ i ] [ 1 ] rm{next}[i][1] next [ i ] [ 1 ]

    2024年02月12日
    浏览(37)
  • 11.动态规划:树形DP问题、树上最大独立集、树上最小支配集、换根DP、树上倍增(LCA)【灵神基础精讲】

    回溯和树形DP的区别(什么时候需要return结果?):对于回溯,通常是在「递」的过程中增量地构建答案,并在失败时能够回退,例如八皇后。对于递归,是把原问题分解为若干个相似的子问题,通常会在「归」的过程中有一些计算。如果一个递归能考虑用记忆化来优化,就

    2024年02月04日
    浏览(42)
  • leetcode刷题(字符串相加、包含每个查询的最小区间、模拟行走机器人、环形子数组的最大和、满足不等式的最大值、四数之和、树中距离之和)

    目录 1、字符串相加 2、包含每个查询的最小区间 3、模拟行走机器人 4、环形子数组的最大和 5、满足不等式的最大值 6、四数之和 7、 树中距离之和

    2024年02月10日
    浏览(47)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包