学习笔记:ST 表

ST 表

ST 表是用于解决 可重复贡献问题 的数据结构。

ST 表可以做到 O(nlogn) 预处理,O(1) 回答每个询问。但不支持修改操作。

基于倍增思想,我们考虑如何求出区间最大值。可以发现,如果按照一般的倍增流程,每次跳 2i 步的话,询问时的复杂度仍旧是 O(logn),并没有比线段树更优,反而预处理一步还比线段树慢。

然而,我们发现显然有 max(x,x)=x,也就是说,区间最大值是一个具有「可重复贡献」性质的问题。即使用来求解的预处理区间有重叠部分,只要这些区间的并是所求的区间,最终计算出的答案就是正确的。

如果手动模拟一下,可以发现我们能使用至多两个预处理过的区间来覆盖询问区间,也就是说询问时的时间复杂度可以被降至 \Theta(1),在处理有大量询问的题目时十分有效。

具体实现如下:

fi,j 表示区间 [i,i+2j1] 的最大值。

显然有 fi,0=ai

根据定义式,第二维就相当于倍增的时候「跳了 2j1 步」,依据倍增的思路,写出状态转移方程:fi,j=max(fi,j1,fi+2j1,j1)

img

以上就是预处理部分。而对于查询,可以简单实现如下:

对于每个询问 [l,r],我们把它分成两部分:[l,l+2s1][r2s+1,r],其中 s=log2(rl+1) 两部分的结果的最大值就是回答。

ST 表的查询过程

根据上面对于「可重复贡献问题」的论证,由于最大值是「可重复贡献问题」,重叠并不会对区间最大值产生影响。又因为这两个区间完全覆盖了 [l,r],可以保证答案的正确性。

#include <iostream>
#define MAXN 100005
using namespace std;
int n, m, x, y, op;
int lg[MAXN];
int st[MAXN][25];
int read(){
    int t = 1, x = 0;char ch = getchar();
    while(!isdigit(ch)){if(ch == '-')t = -1;ch = getchar();}
    while(isdigit(ch)){x = (x << 1) + (x << 3) + (ch ^ 48);ch = getchar();}
    return x * t;
}
void write(int x){
    if(x < 0){putchar('-');x = -x;}
    if(x >= 10)write(x / 10);
    putchar(x % 10 + '0');
}
int main(){
    n = read();m = read();
    for(int i = 1 ; i <= n ; i ++)st[i][0] = read();
    for(int i = 2 ; i <= n ; i ++)lg[i] = lg[i >> 1] + 1;
    for(int i = 1 ; i <= lg[n] ; i ++)
        for(int j = 1 ; j <= n - (1 << i) + 1 ; j ++)
            st[j][i] = max(st[j][i - 1], st[j + (1 << (i - 1))][i - 1]);
    for(int i = 1 ; i <= m ; i ++){
        x = read();y = read();op = lg[y - x + 1];
        write(max(st[x][op], st[y - (1 << op) + 1][op]));putchar('\n');
    }
    return 0;
}

注意

  1. 输入输出数据一般很多,建议开启输入输出优化。
  2. 每次重新计算 log 函数值并不值得,建议进行如下的预处理:

    lgi={0,i=1i2+1,otherwise

posted @   tsqtsqtsq  阅读(10)  评论(0编辑  收藏  举报  
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示