数据结构 ST表 详解

本文深入探讨Sparse Table(简称ST表)这一高效数据结构,基于倍增思想解决可重复贡献问题,如RMQ(区间最值查询)。文章介绍了ST表的基本概念、原理及其实现方法,并通过代码示例展示了如何在O(nlogn)预处理时间内快速解答区间查询问题。

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

😊 | Powered By HeartFireY | Sparse-table
📕 | 需要的前导知识:倍增

一、什么是ST表

1.可重复贡献问题

可重复贡献问题是对于运算optoptopt,运算的性质满足x opt x = xx\ opt\ x\ =\ xx opt x = x,则对应的区间询问就是一个可重复的贡献问题,例如:最大值满足max(x,x)=xmax(x, x) = xmax(x,x)=x,最大公因数满足gcd(x,x)=xgcd(x, x) = xgcd(x,x)=x,因此RMQ问题和GCD问题就是一个可重复贡献的问题,但是例如区间和就不满足这个性质,因为在求解区间和的过程中采用的预处理区间会发生重叠,导致重叠部分被重复计算,因此对于optoptopt操作还需要满足集合率才能够使用ST表进行求解。

2.ST表简介

ST表是一种基于倍增思想,用于解决可重复贡献问题的数据结构。

ST表应用最广泛的领域便是解决RMQ问题:给定nnn个数,mmm个询问,对于每个询问,需要回答区间[l,r][l, r][l,r]中的最大值或最小值(可以采用两个数组同时进行处理)。

基于倍增的思想,ST表可以实现O(nlogn)O(n log n)O(nlogn)下进行预处理,并在0(1)0(1)0(1)时间内回答每个询问。如果仅仅进行区间最值查询,ST表的效率完全吊打线段树;但是,相比于线段树,ST表并不支持修改操作,无论是单点修改还是区间修改都不支持。

二、ST表的原理和实现

我们在简介中提到,ST表基于倍增的思想,但并不是朴素的倍增方式:对于朴素的倍增方式,我们可以发现查询的过程中,我们仍然需要调2i2^i2i步,那么查询的复杂度显然不是0(1)0(1)0(1),也不存在比线段树更有的说法,反倒预处理不如线段树快。

❓::那么倍增的思想是如何应用的呢?

我们在简介前提到了重复贡献问题,显然对于区间的最小值/最大值都满足这个性质:min(x,x)=x, max(x,x)=xmin(x,x) = x,\ max(x, x) = xmin(x,x)=x, max(x,x)=x。那么显然用来求解的预处理的区间有重叠部分,只要这些区间的并是所求区间,最终计算出的答案一定就是正确答案。

❓::具体是如何实现的?

我们定义f[i][j]f[i][j]f[i][j]表示区间[i,i+2j−1][i, i + 2^j - 1][i,i+2j1]的最大值,那么显然f[i][0]=aif[i][0] = a_if[i][0]=ai

那么我们可以根据倍增的思路给出状态转移方程:f(i,j)=max(f(i,j−1), f(i+2j−1,j−1))f(i, j) = max(f(i, j - 1),\ f(i + 2^{j - 1},j-1))f(i,j)=max(f(i,j1), f(i+2j1,j1))

例子

而对于询问[l,r][l, r][l,r],我们将其分成两部分f[l,l+2s−1]f[l, l + 2^s - 1]f[l,l+2s1]f[r−2s+1,r]f[r - 2^s + 1, r]f[r2s+1,r]​,即为两个子区间。由于RMQ属于可重复贡献问题,因此不必使两个区间不相交。

但显然,我们需要使第一个字区间的右端点尽可能的接近rrr​,那么不妨直接令l+2s−1=rl + 2^s - 1 = rl+2s1=r,那么可以得到s=log⁡2(r−l+1)s = \log_2{(r - l + 1)}s=log2(rl+1);同时,我们希望第二个区间的左端点尽可能地接近lll​,也就是r−2s+1=lr - 2^s + 1 = lr2s+1=l,发现于上一个式子使完全相同的,因此只需要取s=log⁡2(r−l+1)s = \log_2{(r -l + 1)}s=log2(rl+1)即可。但显然这样的sss​不是整数​,那么我们直接取向下取整即可,此时的sss仍能保证两个子区间完全覆盖整个区间。

由于使用STL反复计算logloglog值容易卡logloglog,对于sss我们只需预处理出可能用到的全部sss​​值即可:
{Logn[1]=0,Logn[i]=Logn[i2]+1. \left\{\begin{aligned} Logn[1] &=0, \\ Logn\left[i\right] &=Logn[\frac{i}{2}] + 1. \end{aligned}\right. Logn[1]Logn[i]=0,=Logn[2i]+1.

ST表的其他应用

除 RMQ 以外,还有其它的“可重复贡献问题”。例如“区间按位和”、“区间按位或”、“区间 GCD”,ST 表都能高效地解决。

需要注意的是,对于“区间 GCD”,ST 表的查询复杂度并没有比线段树更优(令值域为 www,ST 表的查询复杂度为 Θ(log⁡w)\Theta(\log w)Θ(logw),而线段树为 Θ(log⁡n+log⁡w)\Theta(\log n+\log w)Θ(logn+logw),且值域一般是大于 nnn 的),但是 ST 表的预处理复杂度也没有比线段树更劣,而编程复杂度方面 ST 表比线段树简单很多。

如果分析一下,“可重复贡献问题”一般都带有某种类似 RMQ 的成分。例如“区间按位与”就是每一位取最小值,而“区间 GCD”则是每一个质因数的指数取最小值。

三、ST表RMQ模板

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

#define N 2000010

int stmax[N][22], stmin[N][22], mn[N],a[N];

int q, n;

void init(){
    mn[0] = -1;
    for (int i = 1; i <= n; i++){
        mn[i] = ((i & (i - 1)) == 0) ? mn[i - 1] + 1 : mn[i - 1];
        stmax[i][0] = stmin[i][0] = a[i];
    }
    for (int j = 1; j <= mn[n]; j++)
        for (int i = 1; i + (1 << j) - 1 <= n; i++){
            stmax[i][j] = max(stmax[i][j - 1], stmax[i + (1 << (j - 1))][j - 1]);
            stmin[i][j] = min(stmin[i][j - 1], stmin[i + (1 << (j - 1))][j - 1]);
        }
}

inline int rmq_max(int L, int R){
    int k = mn[R - L + 1];
    return max(stmax[L][k], stmax[R - (1 << k) + 1][k]);
}

inline int rmq_min(int L, int R){
    int k = mn[R - L + 1];
    return min(stmin[L][k], stmin[R - (1 << k) + 1][k]);
}

signed main(){
    cin >> n >> q;
    for(int i = 1; i <= n; i++) cin >> a[i];
    init();
    while(q--){
        int l, r; cin >> l >> r;
        cout << rmq_max(l, r) << rmq_min(l, r) << endl;
    }
    return 0;
}
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

HeartFireY

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

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

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

打赏作者

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

抵扣说明:

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

余额充值