【板子】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;
}
相关推荐
MichaelJohn1 小时前
我用 Obsidian + Claude Code + GitHub 搭了个真正「会生长」的第二大脑
笔记
lengxuenong1 小时前
树状数组 · 信息学学习手册2-离散化与逆序对
c++·学习·算法
琳达爱分享1 小时前
五个线程同时去换 token,四个被踢下线:刷新接口其实不是幂等的
后端·算法·架构
十二1 小时前
【洛谷题解】P1115 最大子段和(前缀和 + 维护最小前缀)
数据结构·c++·算法·蓝桥杯
renhongxia12 小时前
RAG检索增强生成:打通大模型与外部知识的通道
大数据·人工智能·算法
zhangrelay2 小时前
手机长周期测评十周年终结版本-2026
笔记·学习·智能手机
海海不掉头发2 小时前
图论与应用_课堂笔记
笔记·图论
2501_933923252 小时前
双指针算法:移动零+复写零(Java)
数据结构·算法·双指针
和芯星通unicore2 小时前
卫星信号消失的地方,定位如何继续?——解密和芯星通DR 航位推算算法
人工智能·算法·gnss·智能驾驶gnss模块
杨逢昌工厂6S管理3 小时前
127-钣金产线6S改善后总反弹?杨逢昌一线现场诊断笔记:反弹背后的熵增机理与基准化治理
经验分享·笔记·职场和发展·学习方法