[ST表]

本文介绍了解决RMQ问题的一种高效算法——ST表。RMQ问题要求在给定区间内快速查找最大值或最小值。ST表通过预处理建立倍增区间,使用状态转移方程加速查询过程。文章详细解释了ST表的预处理步骤和查询方法,并提供了完整的代码示例。

【模板】RMQ问题的ST表实现

RMQ问题:给定一个长度为N的区间,M个询问,每次询问[Li,Ri]这段区间元素的最大值/最小值。

RMQ的高级写法一般有两种,即为线段树和ST表。

本文主要讲解一下ST表的写法。(以区间最大值为例)

ST表:一种利用dp思想求解区间最值的倍增算法。

定义:f[i][j]表示[i,i+2j1]这段长度为2j的区间中的最大值。

预处理:f(i,0)=ai。即[i,i]区间的最大值就是ai

状态转移:将[i,j]平均分成两段,一段为[i,i+2j11],另一段为[i+2j1,i+2j1]

两段的长度均为2j1f[i][j]的最大值即这两段的最大值中的最大值。

得到f[i][j]=max(f[i][j1],f[i+2j1][j1])

void RMQ(int N){
    /*注意外部循环从j开始,
    因为初始状态为f[i][0],
    以i为外层会有一些状态遍历不到。*/ 
    for(int j=1;j<=20;j++)  
        for(int i=1;i<=N;i++)
            if(i+(1<<j)-1<=N)
                f[i][j]=max(f[i][j-1],f[i+(1<<(j-1))][j-1]);
}

查询:若需要查询的区间为[i,j],则需要找到两个覆盖这个闭区间的最小幂区间。

这两个区间可以重复,因为两个区间是否相交对区间最值没有影响。(如下图)

当然求区间和就肯定不行了。这也就是RMQ的限制性。

因为区间的长度为ji+1,所以可以取k=log2(ji+1)

RMQ(A,i,j)=max{f(i,k),f(j2k+1,k)}

#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
using namespace std;
int a[100001],f[100001][20];
inline int read(){
    int x=0,f=1;
    char c=getchar();
    for(;!isdigit(c);c=getchar())
        if(c=='-')
            f=-1;
    for(;isdigit(c);c=getchar())
        x=x*10+c-'0';
    return x*f;
}
void RMQ(int N){
    for(int j=1;j<=20;j++)    
        for(int i=1;i<=N;i++)
            if(i+(1<<j)-1<=N)
                f[i][j]=max(f[i][j-1],f[i+(1<<(j-1))][j-1]); 
}
int main(){
    int N=read(),M=read();
    for(int i=1;i<=N;i++) a[i]=read();
    for(int i=1;i<=N;i++) f[i][0]=a[i];
    RMQ(N); 
    while(M--){
        int i=read(),j=read();
         int k=(int)(log((double)(j-i+1))/log(2.0)); 
        printf("%d\n",max(f[i][k],f[j-(1<<k)+1][k]));
    }
    return 0;
}

 

转载于:https://www.cnblogs.com/-Wind-/p/10323681.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值