预处理所有 2 的幂长度区间的最大值建成 ST 表,每次询问用两个可重叠的 2 的幂块 O(1) 求出区间最大值。
OJ: roj
题目 ID: 1541
难度:普及+/提高-
标签:ST表RMQ倍增python
创建: 2026-09-30 17:08
更新: 2026-10-07 11:01
形式化题目
给定长度为
所有询问期间数列不发生修改,即静态区间最值查询(RMQ)。
正解
思路
朴素做法与瓶颈。 每次询问从
关键观察:
倍增建表。 长度
其中
| 0(长 1) | 3 | 2 | 4 | 5 | 6 | 8 | 1 | 2 | 9 | 7 |
| 1(长 2) | 3 | 4 | 5 | 6 | 8 | 8 | 2 | 9 | 9 | |
| 2(长 4) | 5 | 6 | 8 | 8 | 8 | 9 | 9 | |||
| 3(长 8) | 8 | 9 | 9 |
观察点:第 max 得到,
例如
查询两段覆盖。 对询问 max:
因为
查对数与查表。 logs[len] = logs[len >> 1] + 1 一次性线性预处理,
查询时不做浮点运算;
代码
Python 版:
#!/usr/bin/env python3
# Author by Rainboy blog: https://rainboylv.com github: https://github.com/rainboylvx
# rbook: -> https://rbook.roj.ac.cn https://rbook2.roj.ac.cn
# rainboy的学习导航网站: https://idx.roj.ac.cn
# create_at: 2026-09-30 17:08
# update_at: 2026-09-30 17:08
import sys
from collections.abc import Iterator
def build_st(a: list[int]) -> list[list[int]]:
"""ST 表:st[k][i] = a[i..i+2^k-1] 的最大值,由下一层两个半段合并。"""
st = [a]
span = 2 # 当前层区间长度
while span <= len(a):
prev = st[-1]
half = span >> 1
st.append([max(prev[i], prev[i + half]) for i in range(len(a) - span + 1)])
span <<= 1
return st
def solve() -> None:
data: Iterator[int] = iter(map(int, sys.stdin.buffer.read().split()))
n, m = next(data), next(data)
a = [next(data) for _ in range(n)] # 题目给的是 1-based 下标
st = build_st(a)
# logs[x] = floor(log2(x)):由 x//2 的结果递推,避免每条询问做浮点对数
logs = [0] * (n + 1)
for i in range(2, n + 1):
logs[i] = logs[i >> 1] + 1
out: list[str] = []
append = out.append
for _ in range(m):
l, r = next(data) - 1, next(data) - 1
k = logs[r - l + 1] # 不超过区间长度的最大 2 的幂
# 两段等长 2^k 允许重叠:max 重复取元素不影响结果
append(str(max(st[k][l], st[k][r - (1 << k) + 1])))
sys.stdout.write('\n'.join(out))
if __name__ == "__main__":
solve()C++ 版(同一算法):
/**
* Author by Rainboy blog: https://rainboylv.com github: https://github.com/rainboylvx
* rbook: -> https://rbook.roj.ac.cn https://rbook2.roj.ac.cn
* rainboy的学习导航网站: https://idx.roj.ac.cn
* create_at: 2026-10-05 07:11
* update_at: 2026-10-05 07:11
*/
#include <cstdio>
#include <algorithm>
using namespace std;
typedef long long ll;
const int MAXN = 100005; // 数列长度上限
const int LOG = 17; // 2^17 > 1e5,层数足够覆盖所有区间长度
int n, m;
int a[MAXN]; // 原数列,1-based
int st[LOG][MAXN]; // st[k][i]:从 i 开始、长度 2^k 的区间最大值
int logs[MAXN]; // logs[x] = floor(log2(x)),预处理避免查询时算浮点对数
// 求 floor(log2(x))(x >= 1),只用在建表前确定层数
int log2_floor(int x) {
int res = 0;
while ((1 << (res + 1)) <= x) res++;
return res;
}
int main() {
scanf("%d %d", &n, &m);
for (int i = 1; i <= n; i++) scanf("%d", &a[i]);
// 建表:第 0 层是长度 1 的区间,就是原数列本身
int K = log2_floor(n); // 最大有用的层号,2^K <= n < 2^(K+1)
for (int i = 1; i <= n; i++) st[0][i] = a[i];
// 长度 2^k 的区间由两个长度 2^(k-1) 的半段取 max 拼成
for (int k = 1; k <= K; k++)
for (int i = 1; i + (1 << k) - 1 <= n; i++)
st[k][i] = max(st[k - 1][i], st[k - 1][i + (1 << (k - 1))]);
// logs 递推:x 的一半的对数再加一,线性预处理,查询 O(1)
logs[1] = 0;
for (int x = 2; x <= n; x++) logs[x] = logs[x >> 1] + 1;
// 每条询问:两段等长 2^k 区间允许重叠地覆盖 [l, r]
// max 幂等,重叠部分重复取不影响答案
while (m--) {
int l, r;
scanf("%d %d", &l, &r);
int k = logs[r - l + 1]; // 不超过区间长度的最大 2 的幂
printf("%d\n", max(st[k][l], st[k][r - (1 << k) + 1]));
}
return 0;
}复杂度
- 时间:建表
,查询每条 ,合计 。 - 空间:
个表项, 时约 个整数。 本题 Python 实现实测峰值约 223 MB(主要是一次性读入切分出的数百万个字节串), 超过题面 128 MB 内存限制;按仓库的 python-oj-short 规则允许 MLE, C++ 用静态 int数组实现同一算法约数 MB 即可。
总结
静态 RMQ 的标准解法是 ST 表: