[USACO07JAN] Balanced Lineup G

OJ: luogu

题目 ID: P2880

标签: st表

日期: 2026-01-17 20:25

题目解析

入门题

代码

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

using namespace std;

const int MAXN = 50005; // 根据题目 N <= 50000
const int LOGN = 17;    // 2^16 = 65536 > 50000,开 17 足够了

// 定义两个 ST 表
int st_max[MAXN][LOGN];
int st_min[MAXN][LOGN];
int Log[MAXN]; // 预处理 log 值
int n, q;

// 预处理函数
void init() {
    // 1. 预处理 Log 数组 (我们刚才证明过的公式)
    Log[1] = 0;
    for (int i = 2; i <= n; i++) {
        Log[i] = Log[i / 2] + 1;
    }

    // 2. DP 构建 ST 表
    // 注意:st_max[i][0] 和 st_min[i][0] 在输入时已经填好了
    
    for (int j = 1; j < LOGN; j++) { // 枚举长度指数 2^j
        for (int i = 1; i + (1 << j) - 1 <= n; i++) { // 枚举起点
            // 状态转移:当前区间 = max(左半边, 右半边)
            // 右半边的起点是 i + 2^(j-1)
            int half_len = 1 << (j - 1);
            
            st_max[i][j] = max(st_max[i][j - 1], st_max[i + half_len][j - 1]);
            st_min[i][j] = min(st_min[i][j - 1], st_min[i + half_len][j - 1]);
        }
    }
}

// 查询最大值
int query_max(int l, int r) {
    int k = Log[r - l + 1];
    // 覆盖左边和右边,取 max
    return max(st_max[l][k], st_max[r - (1 << k) + 1][k]);
}

// 查询最小值
int query_min(int l, int r) {
    int k = Log[r - l + 1];
    // 覆盖左边和右边,取 min
    return min(st_min[l][k], st_min[r - (1 << k) + 1][k]);
}

int main() {
    // 1. 加速 IO
    ios::sync_with_stdio(false);
    cin.tie(0);

    // 2. 输入
    cin >> n >> q;
    for (int i = 1; i <= n; i++) {
        int h;
        cin >> h;
        // 初始化 ST 表的第 0 层(长度为 1 的区间)
        st_max[i][0] = h;
        st_min[i][0] = h;
    }

    // 3. 构建 ST 表
    init();

    // 4. 处理询问
    for (int i = 0; i < q; i++) {
        int a, b;
        cin >> a >> b;
        // 题目保证 a <= b,不需要 swap
        // 直接输出差值
        cout << query_max(a, b) - query_min(a, b) << "\n";
    }

    return 0;
}