OJ: luogu

题目 ID: P1816

标签: st表

日期: 2026-01-17 20:10

题目解析

RMQ ,ST表模板题目

代码

cpp
#include <iostream>
#include <algorithm>
#include <vector>
#include <cmath>

using namespace std;

const int MAXN = 100005;
const int LOGN = 20; // 2^17 > 100000,开到 20 足够了

// f[i][j] 表示从 i 开始,长度为 2^j 的区间最小值
int f[MAXN][LOGN];
// Log[i] 存储 i 的以 2 为底的对数 (向下取整)
int Log[MAXN]; 
int n, m;

void init() {
    // 1. 预处理 Log 数组
    // Log[1] = 0, Log[2]=1, Log[3]=1, Log[4]=2 ...
    Log[1] = 0;
    for (int i = 2; i <= m; i++) {
        Log[i] = Log[i / 2] + 1;
    }

    // 2. 初始化 ST 表的第一列 (长度为 2^0 = 1 的区间)
    // 这一步在输入时已经完成了,即 f[i][0] = input[i]

    // 3. 动态规划填表
    // j 是长度指数,必须在外层循环
    for (int j = 1; j <= LOGN - 1; j++) {
        // i 是起点
        // 注意边界:i + 2^j - 1 不能超过 m
        for (int i = 1; i + (1 << j) - 1 <= m; i++) {
            // 状态转移:左半部分 和 右半部分 取 min
            // 右半部分的起点是 i + 2^(j-1)
            f[i][j] = min(f[i][j - 1], f[i + (1 << (j - 1))][j - 1]);
        }
    }
}

int query(int l, int r) {
    int len = r - l + 1;
    int k = Log[len]; // 找到最大的 k 使得 2^k <= len
    
    // 覆盖左边 [l, l + 2^k - 1] 和 右边 [r - 2^k + 1, r]
    return min(f[l][k], f[r - (1 << k) + 1][k]);
}

int main() {
    // 优化输入输出效率
    ios::sync_with_stdio(false);
    cin.tie(0);

    // 题目中输入是 m, n
    // m 是账目数量(数组大小), n 是询问次数
    cin >> m >> n;

    for (int i = 1; i <= m; i++) {
        cin >> f[i][0]; // 读入数据直接存入 ST 表的第 0 层
    }

    // 构建 ST 表
    init();

    // 处理询问
    for (int i = 0; i < n; i++) {
        int a, b;
        cin >> a >> b;
        cout << query(a, b) << " ";
    }
    cout << endl;

    return 0;
}