浅谈ST表

浅谈ST表

先来一个小问题 :

有N个数,M次询问,每次给定区间[L,R],求区间内的最大值。
N<=10,M<=10

老师,我会O(N)暴力枚举!
再来:

N<=10^ 5,M<=10^5

老师,我会线段树O(logN)处理每个询问!

再来:

N<=10^ 5,M<=10^6

这时候我们发现,随着M的增大,O(logN)的询问的处理已经不够优秀,我们需要O(1)处理询问的方法。这就引出我们今天的主题——ST表。

算法流程:

我们现在要O(1)求出区间最大值,一个很自然的想法便是记录 f( i , j )为 [ i , j ]区间中的最大值,显然有转移方程 f ( i , j ) = max( f ( i , j-1 ) , aj )

但是这样的预处理是O(N2)的,不能通过,我们考虑进一步优化

观察一个性质:max操作允许区间重叠,也就是max(a,b,c)=max(max(a,b),max(b,c)) (这个性质非常重要,决定了ST表是否能用来维护这种操作,例如ST表一般不能维护区间和,因为a+b+c != a+b+b+c ),也就是说我们可以由两个较小的、有重叠的区间直接推出一个大区间,因此我们可以少维护一些区间

计算机中有很多事物都是和2有关的。这里也是这样,我们采用倍增的思想,令 f ( i , j )为从 ai 开始连续 2j 个数的最大值,显然:

f ( i , 0 ) = ai

f ( i , j ) = max( f ( i , j-1 ) , f ( i + 2j-1 , j-1) )

这一条非常重要,我们画个图解释一下:
在这里插入图片描述
现在我们考虑f(1,2),也就是[1,4]的最大值
在这里插入图片描述
我们把[1,4]分为了[1,2]和[3,4]两个小区间,这两个区间是我们之前求过的f(1,1)与f(3,1),而f(1,1)=8,f(3,1)=7,则f(1,2)=max ( f (1,1) , f (3,1) )=8

现在发现,在这种方式下,以每个点为起点都有O(logN)个区间,每个区间可以O(1)求出,则预处理总时间、空间复杂度都为O(N logN)。

那怎么处理询问呢?

根据max的性质,我们可以把区间拆成两个相叠的区间。看图:
在这里插入图片描述
记查询的左区间长度为len,我们从左端点,我们从左端点向右找一段2log(len)的区间(蓝色部分),右端点向左也找一段长为2log(len)的区间(黄色部分),显然这两端区间已经覆盖了整个区间(中间重叠的一块绿色部分),取最大值即可

当然为了保证查询的复杂度为O(1),我们需要提前预处理出每个log(len)向下取整后的值,这个算法总时间复杂度为O(N logN+M)

顺便附上代码:

#include<bits/stdc++.h>
using namespace std;
const int N=1e5+10;
int a[N],lg[N];
int maxn[N][50];
int main()
{
    int n,m;
    scanf("%d%d",&n,&m);
    lg[0]=-1;
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&a[i]);
        lg[i]=lg[i/2]+1;
    }
    for(int i=1;i<=n;i++) maxn[i][0]=a[i];
    for(int i=1;i<=lg[n];i++)
    {
        for(int j=1;j+(1<<i)-1<=n;j++)
            maxn[j][i]=max(maxn[j][i-1],maxn[j+(1<<(i-1))][i-1]);
    }
    int l=0,r=0;
    while(m--)
    {
        scanf("%d%d",&l,&r);
        int len=lg[r-l+1];
        printf("%d\n",max(maxn[l][len],maxn[r-(1<<(len))+1][len]));
    }
    //system("pause");
    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值