ST表简介 (洛谷P3865、洛谷P2251)

本文介绍SparseTable算法,一种利用倍增思想解决RMQ等在线查询问题的方法。文章详细解释了SparseTable的预处理过程和查询操作,并提供了求解最大值和最小值问题的代码实例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

算法用途

Sparse Table,又称ST表,稀疏表。运用倍增的思想,可以解决RMQ,LCA等问题。其优点是在线查询。预处理复杂度为O(nlogn),查询复杂度为O(1)。

算法思想

运用倍增的思想,num[i][j]表示区间[i,i+(1 << j)]的值。然后进行预处理求出num数组。

算法实现

以求最大值为例

① 对于预处理,有如下转移方程式:

num[i][j]=max(num[i][j-1],num[i+(1<<j-1)][j-1]);

这是什么东西啊??

我们来推一遍:
其实只是把[i,i+(1 << j)]这个区间给分成两块,一块是[i,i+(1 << j-1)],另一块是[i+(1 << j-1),i+(1 << j)](i+(1 << j)==i+(1 << j-1)+(1 << j-1))。

所以这个区间的最大值就是这两个区间的最大值的较大者。

然后就推好啦!

② 对于查询[x,y]之间的最大值,我们可以这样:

int j=log2(y-x+1);
printf("%d\n",max(num[x][j],num[y-(1<<j)+1][j]));

这又是什么东西??

我们再来推一遍:
num[x][j]表示[x,x+(1 << j)]这段区间,本来是刚好的,但是因为j是向下取整的,因此不一定取得完。取到的区间长度实际为1 << j-1。此时我们可以从y-(1 << j-1)=y-(1 << j)+1这个点开始取(取重了也没事),取相同的长度。然后在这两者之间取较大者即可。

模板

洛谷P3865 为例:

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define MAXN 150000
using namespace std;
int n,m;
int num[MAXN+5][18];
inline char readc(){
    static char buf[100000],*l=buf,*r=buf;
    if (l==r) r=(l=buf)+fread(buf,1,100000,stdin);
    if (l==r) return EOF;
    return *l++;
}
int _read(){
    int num=0; char ch=readc();
    while (ch<'0'||ch>'9') ch=readc();
    while (ch>='0'&&ch<='9'){ num=num*10+ch-48; ch=readc(); }
    return num;
}
void make(){
    for (int i=1;i<18;i++)
        for (int j=1;j+(1<<i)-1<=n;j++)
            num[j][i]=max(num[j][i-1],num[j+(1<<i-1)][i-1]);
}
int main(){
    n=_read(); m=_read();
    for (int i=1;i<=n;i++)
        num[i][0]=_read();
    make();
    for (int i=1;i<=m;i++){
        int x=_read(),y=_read();
        int j=log2(y-x+1);
        printf("%d\n",max(num[x][j],num[y-(1<<j)+1][j]));
    }
    return 0;
}

再来个最小值(洛谷P2251):

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define MAXN 1500000
using namespace std;
int num[MAXN+5][21],q[MAXN+5];
int n,m;
inline char readc(){
    static char buf[100000],*l=buf,*r=buf;
    if (l==r) r=(l=buf)+fread(buf,1,100000,stdin);
    if (l==r) return EOF;
    return *l++;
}
int _read(){
    int num=0; char ch=readc();
    while (ch<'0'||ch>'9') ch=readc();
    while (ch>='0'&&ch<='9') { num=num*10+ch-48; ch=readc(); }
    return num;
}
void make(){
    for (int j=1;j<=20;j++)
        for (int i=1;i<=n;i++)
            num[i][j]=min(num[i][j-1],num[i+(1<<j-1)][j-1]);
}
int main(){
    n=_read(); m=_read();
    for (int i=1;i<=n;i++)
        num[i][0]=_read();
    make();
    for (int i=1;i<=n-m+1;i++){
        int x=i,y=m+i-1;
        int j=log2(y-x+1);
        q[i]=min(num[x][j],num[y-(1<<j)+1][j]);
    }
    for (int i=1;i<=n-m+1;i++)
        printf("%d\n",q[i]);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值