ST表处理RMQ问题模板

文章介绍了如何利用ST表(也称为RMQ,RangeMinimumQuery)解决区间最大值查询的问题。通过倍增思想,预先计算每个2的指数幂区间的最大值,然后在查询时快速找到最长的预处理区间以高效地得到结果。文章提供了代码示例,包括预处理和查询函数,以及输入输出样例。
摘要由CSDN通过智能技术生成

 ST表的实现利用了倍增思想,用于处理可重复贡献问题,例如区间最大值、区间最小值、区间最大公因数等(x opt x = x)问题。

RMQ模板问题

题目描述

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

输入格式

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

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

接下来 M 行,每行包含两个整数 li​,ri​,表示查询的区间为[li​,ri​]。

输出格式

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

输入 #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% 的数据,满足 1≤N,M≤10。

对于 70% 的数据,满足1≤N,M≤105。

对于 100% 的数据,满足 1≤N≤105,1≤M≤2×106,ai​∈[0,109],1≤li​≤ri​≤N。

实现思路

利用倍增思想,通过2的指数进行倍增,maxn[ i ][ j ] 表示 从 i 开始,2^j 个数字中的最大值,即[ i ,2^j ] 中的最大值

由于2的指数幂的特性,maxn[ i ][ j ] 可以拆分为 maxn[ i ][ j - 1] 和 maxn[ i+ 2^(j-1)][ j - 1] 两个区间,大区间的最大值,等于小区间最大值里最大的那个数

查询时,我们希望找到查询区间里被预处理过的最长的区间,即 l + 2^k -1 == r 的k值,计算log2( r - l +1)向下取整则为最长区间,区间长度小于等于查询区间,所以我们在右侧找一个对称的区间,两个区间必定全部包含查询区间

log2(n)一直用stl容易超时,需要进行预处理,等于1<<t 时+1即可

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int read(){         //快读
    char ch=getchar();
    int f=1,x=0;
    while(ch>'9'||ch<'0'){
        if(ch=='-')f=-1;
        ch=getchar();
    }
    while(ch>='0'&&ch<='9'){
        x=(x<<1)+(x<<3)+(ch^48);
        ch=getchar();
    }
    return f*x;
}
int n,m;
int maxn[100005][30];       //RMQ
int Log[100005];        //预处理2的对数
void init(){            //预处理
    Log[0]=-1;
    for(int i=1;i<=100000;i++){
        if((i&(i-1))==0)Log[i]=Log[i-1]+1;      //判断 i 是不是1左移的结果
        else Log[i]=Log[i-1];
    }
    for(int j=1;j<=Log[n];j++){         //外层循环是y,内层循环是x
        for(int i=1;i+(1<<j)-1<=n;i++){     //注意边界
            maxn[i][j]=max(maxn[i][j-1],maxn[i+(1<<(j-1))][j-1]);   //分成两部分
        }
    }
}
int q(int l,int r){     //查询
    int k=Log[r-l+1];   //分两个区间。先查询再合并
    return max(maxn[l][k],maxn[r-(1<<k)+1][k]);
}
int main(){
    n=read(),m=read();
    for(int i=1;i<=n;i++){
        maxn[i][0]=read();      //直接把maxn[i][0]读进去
    }
    init();
    while(m--){
        int l=read(),r=read();
        printf("%d\n",q(l,r));      //O(1)查询
    }
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Auroraaaaaaaaaaaaa

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值