ST表 Sparse Table

ST表 Sparse Table

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

可重复贡献问题 是指对于运算 opt\operatorname{opt},满足 xoptx=xx\operatorname{opt} x=x,则对应的区间询问就是一个可重复贡献问题。例如,最大值有 max(x,x)=x\max(x,x)=x,gcd 有 gcd(x,x)=x\operatorname{gcd}(x,x)=x,所以 RMQ 和区间 GCD 就是一个可重复贡献问题。像区间和就不具有这个性质,如果求区间和的时候采用的预处理区间重叠了,则会导致重叠部分被计算两次,这是我们所不愿意看到的。另外,opt\operatorname{opt} 还必须满足结合律才能使用 ST 表求解。

模板题P3865:

给定一个长度为 NN 的数列,和 MM 次询问(查询区间[li,ri][l_i,r_i]),求出每一次询问的区间内数字的最大值。

ST表基于倍增的思想,也类似线段树,但是比倍增和线段数强的地方在于可以做到每次查询O(1)O(1)的复杂度。

因为max操作具有可重复贡献性,所以即使查询用到的区域有重叠,给出的结果也是正确的。实际上任何区域可以用两个可能有重叠的区域来覆盖,这样对于大量查询的问题就更快。

基于ST表的解法:

查询过程如下图所示:

实现代码:

#include <bits/stdc++.h>
using namespace std;
const int logn = 21;
const int maxn = 2000001;
int f[maxn][logn + 1], Logn[maxn + 1];
 
inline int read() {  // 快读
  char c = getchar();
  int x = 0, f = 1;
  while (c < '0' || c > '9') {
    if (c == '-') f = -1;
    c = getchar();
  }
  while (c >= '0' && c <= '9') {
    x = x * 10 + c - '0';
    c = getchar();
  }
  return x * f;
}
 
void pre() {  // 准备工作,初始化
  Logn[1] = 0;
  Logn[2] = 1;
  for (int i = 3; i < maxn; i++) {
    Logn[i] = Logn[i / 2] + 1;
  }
}
 
int main() {
  int n = read(), m = read();
  for (int i = 1; i <= n; i++) f[i][0] = read();
  pre();
  for (int j = 1; j <= logn; j++)
    for (int i = 1; i + (1 << j) - 1 <= n; i++)
      f[i][j] = max(f[i][j - 1], f[i + (1 << (j - 1))][j - 1]);  // ST表具体实现
  for (int i = 1; i <= m; i++) {
    int x = read(), y = read();
    int s = Logn[y - x + 1];
    printf("%d\n", max(f[x][s], f[y - (1 << s) + 1][s]));
  }
  return 0;
}
习题
P2880USACO GoldBalanced Lineup普及/提高-
P2048NOI超级钢琴2010省选/NOI-
算法标签前缀和, ST