【板子】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;
}
相关推荐
Jerry7 小时前
LeetCode 92. 反转链表 II
算法
小王C语言7 小时前
【3. 基于 Vibe Coding 的 OJ 平台】. 构建仓库、环境准备、需求梳理、安装依赖
网络·c++
骊城英雄7 小时前
Rust从入门到精通-trait
人工智能·算法·rust
可编程芯片开发7 小时前
基于PI控制算法的pwm直流电机控制系统Simulink建模与仿真
算法
小王C语言7 小时前
【8.进行接口测试】:通过 curl 进行接口自动化测试 / 通过 python 程序进行接口自动化测试
网络·c++
怕浪猫8 小时前
2840亿参数只卖白菜价:DeepSeek V4 Flash 正式版上线,Agent 能力暴涨6倍
人工智能·算法
让学习成为一种生活方式8 小时前
苄基异喹啉生物碱糖基转移酶UGT74AN1晶体--Journal of Agricultural and Food Chemistry
人工智能·算法
早睡早起身体好1238 小时前
为什么 Network 中会出现两个同名请求?——CORS 预检请求学习笔记
网络·笔记·学习·网络安全
alphaTao8 小时前
LeetCode 每日一题 2026/7/27-2026/8/2
python·算法·leetcode
888CC++8 小时前
C++ 快速学习指南:从入门到进阶的实战路线
开发语言·c++