ST表(区间最值问题)

对于一组长度为N的队列,进行M次询问,每次询问一个区间,查找该区间内的最大数值

如果用暴力枚举的话,时间复杂度最坏能到O(N*N*M),当数据量大时肯定会TLE

ST表是一种预处理复杂度为O(logn),查询复杂度为O(1)的一种查区间最值的方法,但是这种方法有缺陷,仅限于数据元素不变的静态表,当数据元素会实时发生变化时并不适用(用线段树解决动态区间最值问题,蒻苟还没学线段树,不会这个)

ST表,预处理二维数组 st [ i ][ j ],其中表示数组中第 i 位置开始 2^j 个位置内的最值,所以对于int类型,j的范围就在30以下,该预处理二维数组的大小还是挺小的。

 有点类似倍增,我们发现 st [ 3 ][ 2 ] 可以将2分成两个1,就是把2^2分成了2^1的两个部分,用 max ( st [ 3 ][ 1 ],st [ 5 ][ 1 ] ) 两个部分来表示 st [ 3 ][ 2 ] 的区间最大值。

查询的时候先计算出查询区间的长度(我们需要找到一个最大的k满足2^k<=len),因为不能保证区间长度是2的次方,所以我们需要在区间内找到一个x值,使得 x + 2^k -1= r,因此

x=r-2^k+1 ,我们只需要查询 max ( st [ l ][ m ] ,st [ r - ( 1<<m ) + 1 ][ m ] ))即可

#include <bits/stdc++.h>
#define rep(i,n,m) for(int i=n;i<=m;++i)
#define per(i,n,m) for(int i=n;i>=m;--i)
using namespace std;
const int N=2e6+10;
int st[N][30];
//这个区间是左闭右开 是[N,N+2^30)
int main()
{
    ios::sync_with_stdio(0);cin.tie(0);
    int n,q;cin>>n>>q;
    rep(i,1,n)cin>>st[i][0]; 第i个位置到i+2^0位置的最值,[i,i+2^0),左闭右开 
    rep(j,1,30){  //枚举区间的长度,从 2^0 → 2^30
        for(int i=1;i+(1<<j)-1<=n;++i){   //
           //防止数组值越界,因此区间右值不能超过数组右端点 i+(1<<j)-1
            st[i][j]=max(st[i][j-1],st[i+(1<<j-1)][j-1]);
        }
    }
    while (q--) {
        int l,r;cin>>l>>r;
        int m=log(r-l+1)/log(2);
        //l到r之间的长度大小(2的次方)
        cout<<max(st[l][m],st[r-(1<<m)+1][m]))<<'\n';
    }
    return 0;
}

当然,也可以用ST表查询最小值,静态区间最值问题都可以解决,要抓紧时间学学线段树了,马上就大二了。。。

附上洛谷 题目:
P3865 【模板】ST 表 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

女人只会影响我敲代码的速度QAQ

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

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

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

打赏作者

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

抵扣说明:

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

余额充值