【板子】ST表

ST 表模板

原理

ST 表用于静态区间最值,预处理 \(O(n\log n)\),单次查询 \(O(1)\),不支持修改。 \(fij\):从第 i 个数开始,长度为 \(2^j\) 的区间最大值。

预处理转移

(fij = max(fij-1, fi + 2\^{j-1}j-1))

查询

区间 (l,r),长度 (len=r-l+1),k=⌊log2​(len)⌋

ans=max(flk, fr−2k+1k)

完整 AC 代码

复制代码
#include <iostream>
#include <cmath>
#include <algorithm>
using namespace std;

const int MAXN = 1e5 + 10;
const int LOG = 20; // 2^20 > 1e6 够用

int n, m;
int a[MAXN];
int st[MAXN][LOG];
int log2_[MAXN]; // 预处理log2,查询更快

// 预处理log数组
void preLog() {
    log2_[1] = 0;
    for (int i = 2; i < MAXN; ++i)
        log2_[i] = log2_[i / 2] + 1;
}

// 构建ST表
void build() {
    // 长度2^0=1
    for (int i = 1; i <= n; ++i)
        st[i][0] = a[i];
    // 倍增
    for (int j = 1; j < LOG; ++j)
        for (int i = 1; i + (1 << j) - 1 <= n; ++i)
            st[i][j] = max(st[i][j-1], st[i + (1 << (j-1))][j-1]);
}

// 查询[l, r]最大值
int query(int l, int r) {
    int k = log2_[r - l + 1];
    return max(st[l][k], st[r - (1 << k) + 1][k]);
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(0);
    preLog();

    cin >> n >> m;
    for (int i = 1; i <= n; ++i)
        cin >> a[i];
    build();

    while (m--) {
        int l, r;
        cin >> l >> r;
        cout << query(l, r) << '\n';
    }
    return 0;
}

二维 ST 表模板

一、适用场景

静态二维矩阵,多次询问任意子矩形最大值 / 最小值,无单点修改

  • 预处理:\(O(nm\log n \log m)\)
  • 单次查询:\(O(1)\)
  • 限制:矩阵长宽不超过 250/1000 都可,LOG 开到 10 足够(\(2^{10}=1024\))

定义

\(stmaxijkl\):左上角\((i,j)\),高\(2^k\)、宽\(2^l\)的矩形最大值 \(stminijkl\):同上,存最小值

查询原理

矩形 x1,y1左上,x2,y2 右下

kx=log2​(x2​−x1​+1), ky=log2​(y2​−y1​+1)

用四块重叠区间覆盖整个矩形,取最大 / 最小:

max=max(

stmaxx1​y1​kxky,

stmaxx2​−2kx+1y1​kxky,

stmaxx1​y2​−2ky+1kxky,

stmaxx2​−2kx+1y2​−2ky+1kxky

)

最小值同理用 min

二、预处理 log 数组

提前预处理 1~maxn 的 log2 下取整,避免浮点运算,加速查询。

三、可直接复制完整模板(1 下标,通用)

复制代码
#include <iostream>
#include <algorithm>
using namespace std;

// 按需修改:矩阵最大长宽、倍增层数
const int MAXN = 255;
const int LOG = 10;

int n, m;          // n行 m列
int a[MAXN][MAXN]; // 原矩阵
int stmax[MAXN][MAXN][LOG][LOG];
int stmin[MAXN][MAXN][LOG][LOG];
int lg[MAXN];      // lg[i] = floor(log2(i))

// 预处理log数组
void pre_log() {
    lg[1] = 0;
    for (int i = 2; i < MAXN; i++)
        lg[i] = lg[i / 2] + 1;
}

// 构建二维ST表
void build() {
    // 初始化 2^0 * 2^0 单点
    for (int i = 1; i <= n; i++)
        for (int j = 1; j <= m; j++)
            stmax[i][j][0][0] = stmin[i][j][0][0] = a[i][j];

    // 第一阶段:行倍增,高度固定 2^0,加宽
    for (int l = 1; l < LOG; l++) {
        for (int i = 1; i <= n; i++) {
            for (int j = 1; j + (1 << l) - 1 <= m; j++) {
                stmax[i][j][0][l] = max(stmax[i][j][0][l-1], stmax[i][j + (1 << (l-1))][0][l-1]);
                stmin[i][j][0][l] = min(stmin[i][j][0][l-1], stmin[i][j + (1 << (l-1))][0][l-1]);
            }
        }
    }

    // 第二阶段:列倍增,宽度所有l,加高
    for (int k = 1; k < LOG; k++) {
        for (int i = 1; i + (1 << k) - 1 <= n; i++) {
            for (int j = 1; j <= m; j++) {
                for (int l = 0; l < LOG; l++) {
                    stmax[i][j][k][l] = max(stmax[i][j][k-1][l], stmax[i + (1 << (k-1))][j][k-1][l]);
                    stmin[i][j][k][l] = min(stmin[i][j][k-1][l], stmin[i + (1 << (k-1))][j][k-1][l]);
                }
            }
        }
    }
}

// 查询矩形 [x1,y1] ~ [x2,y2] 最大值
int get_max(int x1, int y1, int x2, int y2) {
    int kx = lg[x2 - x1 + 1];
    int ky = lg[y2 - y1 + 1];
    int lenx = 1 << kx;
    int leny = 1 << ky;
    return max({
        stmax[x1][y1][kx][ky],
        stmax[x2 - lenx + 1][y1][kx][ky],
        stmax[x1][y2 - leny + 1][kx][ky],
        stmax[x2 - lenx + 1][y2 - leny + 1][kx][ky]
    });
}

// 查询矩形 [x1,y1] ~ [x2,y2] 最小值
int get_min(int x1, int y1, int x2, int y2) {
    int kx = lg[x2 - x1 + 1];
    int ky = lg[y2 - y1 + 1];
    int lenx = 1 << kx;
    int leny = 1 << ky;
    return min({
        stmin[x1][y1][kx][ky],
        stmin[x2 - lenx + 1][y1][kx][ky],
        stmin[x1][y2 - leny + 1][kx][ky],
        stmin[x2 - lenx + 1][y2 - leny + 1][kx][ky]
    });
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    pre_log();

    // 读入矩阵行列
    cin >> n >> m;
    for (int i = 1; i <= n; i++)
        for (int j = 1; j <= m; j++)
            cin >> a[i][j];
    build();

    int q;
    cin >> q;
    while (q--) {
        int x1, y1, x2, y2;
        cin >> x1 >> y1 >> x2 >> y2;
        int mx = get_max(x1, y1, x2, y2);
        int mi = get_min(x1, y1, x2, y2);
        cout << mx - mi << '\n';
    }
    return 0;
}
相关推荐
CHHH_HHH2 小时前
【C++11】深入解析C++可变参数模板
开发语言·c++·算法·stl·c++11
hurrycry_小亦2 小时前
洛谷题目:P1215 [USACO1.4] 母亲的牛奶 Mother‘s Milk 题解(本题简)
算法
十五年专注C++开发2 小时前
fatal error C1189: #error: WinSock.h has already been included 的解决方案
c++·socket·boost
risc1234562 小时前
肌肉收缩会释放大量的肌细胞因子能抑制体内炎症因子
笔记
KJ_BioMed2 小时前
从PDB到高亲和力分子:De novo生成式计算化学Pipeline剖析
算法·生物医药·生物科研·科研干货·化合物设计
一个王同学3 小时前
从零到一 | CV转多模态大模型 | week17 | LLM 推理优化 & vLLM 详解
人工智能·深度学习·算法·机器学习·计算机视觉·vllm
既然如此,那就开摆4 小时前
精读笔记06_CoCa
笔记
疯狂打码的少年4 小时前
【软件工程】软件开发模型:瀑布模型(特点与适用场景)
笔记·软件工程
三品吉他手会点灯4 小时前
嵌入式机器学习 - 学习笔记1.1.1 - 什么是机器学习?
c语言·人工智能·笔记·嵌入式硬件·学习·机器学习