RMQ问题-st表

比赛的时候写了个线段树......然后tle了...不知道世界上还有st表这种东西(大哭

预处理复杂度查询m次复杂度
线段树o(n)o(mlogn)
st表o(nlogn)o(m)

不过st表空间复杂度不如线段树好。

总结:如果只需要查询就用st表,但是st表无法增删改,所以需要多次修改值的时候,就用线段树

#include <bits/stdc++.h>
using namespace std;

int n,m;
const int maxn=1e5;
int a[maxn], st[maxn][20];

void create(){
    int k=log2(n);
    for(int j=1;j<=k;j++){//列数一共有log2n列
        for(int i=1;i+(1<<j)-1<=n;i++){//每一列的长度都不一样,最大只能达到i+(2^j)-1
            st[i][j]=max(st[i][j-1],st[i+(1<<(j-1))][j-1]);
            //当前st的值,是它左边一个st(左边界-中数)
            //和下面第2^(j-1)个st(中数-右边界)中的最大值
        }
    }
    //st[i][j]含义:数字下标i到数字下标i+(2^j)-1区间的最大值
    //即每个数字往后2^j(包含自身)区间内的最大值
}

int result(int L,int R){
    int k=log2(R-L+1);
    return max(st[L][k],st[R-(1<<k)+1][k]);
}

int main(){
    
    cin>>n;
    for(int i=1;i<=n;i++){
        cin>>st[i][0];//根据上文st表含义,[i][0]的范围是i~i,即本身
    }

    create();//建表

    cin>>m;
    while(m--){
        int L,R;
        cin>>L>>R;
        cout<<result(L,R)<<endl;
    }
}

关于建表的注释都写在代码里了。

查询原理:

因为我们要查询的区间大小不一定正好是2的j次方,所以不一定能直接在表中找到对应的最大值。但是如果我们要查询的区间是[l,r],那我们可以查询max(区间[l,a],区间[b,r]),并且a一定要大于等于b,因为中间有交集的部分不影响最大值,但如果中间有断裂的地方就影响了,如果最大值刚好在断裂的区间里就会判断错误。

也就是说,我们可以选择区间[l,l+2^x]和[r-2^x,r]这两段区间,因为他们的差值都为2的次方,一定能在表中找到。

那如何才能保证两端区间相重合呢?我们只需要找到x的最大值就可以了,也就是让这两个区间各自都截取最长,即2^x<r-l+1,x<log2(r-l+1),x取最大值k。

此时左st表的下标就是st[l][k],右st表st[r-(1<<k)+1][k],分别代表了从l到l后2^k个数的区间,和从r到r前2^k个数的区间,计算两者最大值即可。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值