【ACWing】1270. 数列区间最大值

这篇博客介绍了如何利用前缀和技巧解决区间最大值查询(RMQ)问题,给出了一个C++代码实现,该实现具有预处理O(nlogn)的时间复杂度和每次询问O(1)的时间复杂度,空间复杂度为O(nlogn)。

题目地址:

https://www.acwing.com/problem/content/description/1272/

输入一串数字,给你 M M M个询问,每次询问就给你两个数字 X , Y X,Y X,Y,要求你说出 X X X Y Y Y这段区间内的最大数。

输入格式:
第一行两个整数 N , M N,M N,M表示数字的个数和要询问的次数;接下来一行为 N N N个数;接下来 M M M行,每行都有两个整数 X , Y X,Y X,Y

输出格式:
输出共 M M M行,每行输出一个数。

数据范围:
1 ≤ N ≤ 1 0 5 1≤N≤10^5 1N105
1 ≤ M ≤ 1 0 6 1≤M≤10^6 1M106
1 ≤ X ≤ Y ≤ N 1≤X≤Y≤N 1XYN
数列中的数字均不超过 2 31 − 1 2^{31}−1 2311

典型的RMQ问题。代码如下:

#include <iostream>
#include <cmath>
using namespace std;

const int N = 1e5 + 10, M = log2(N) + 1;
int n, m;
int a[N];
int f[N][M];

int main() {
    scanf("%d%d", &n, &m);
    for (int i = 1; i <= n; i++) {
        scanf("%d", &a[i]);
        f[i][0] = a[i];
    }

    int J = log2(n) + 1;
    for (int j = 1; j <= J; j++)
        for (int i = 1; i + (1 << j) - 1 <= n; i++)
            f[i][j] = max(f[i][j - 1], f[i + (1 << j - 1)][j - 1]);

    while (m--) {
        int l, r;
        scanf("%d%d", &l, &r);
        int k = log2(r - l + 1);
        printf("%d\n", max(f[l][k], f[r - (1 << k) + 1][k]));
    }

    return 0;
}

预处理时间复杂度 O ( n log ⁡ n ) O(n\log n) O(nlogn),每次询问时间 O ( 1 ) O(1) O(1),空间 O ( n log ⁡ n ) O(n\log n) O(nlogn)

<think>好的,我现在需要回答用户关于如何用ST表实现数列区间最大值的问题。首先,我需要回忆一下ST表的基本原理和实现步骤。ST算法,也就是Sparse Table Algorithm,主要用于解决RMQ(Range Minimum/Maximum Query)问题,也就是区间最值查询。用户提到的数列区间最大值正是RMQ问题的一个典型应用。 根据引用中的内容,比如引用[2]提到,ST算法的原理是将区间分成两个预处理的区间,这两个区间的并集覆盖原区间,然后取这两个区间最大值中的较大者。引用[3]详细说明了查询时的处理步骤,需要找到最大的k,使得2^k不超过区间长度,然后分解区间为两个可能有重叠的部分,分别查找这两个部分的最大值,再取最大。 接下来,我需要整理ST表的实现步骤,包括预处理和查询两个部分。预处理阶段需要构建一个二维数组f,其中f[i][j]表示从位置i开始,长度为2^j的区间最大值。构建时,可以利用动态规划的思想,先处理长度为1的区间,然后逐步合并相邻区间,直到覆盖整个数组。引用[1]和[4]中的代码示例可能有用,但用户提供的引用中并没有完整的代码,不过可以结合描述来推导。 在预处理阶段,首先初始化每个位置i的f[i][0]为a[i],因为长度为1的区间最大值就是元素本身。然后,对于每个j从1到log2(n),遍历所有可能的i,使得i+2^(j-1)不超过n,这样f[i][j]就是f[i][j-1]和f[i+2^(j-1)][j-1]中的最大值。这一步是动态规划的关键,将大区间分解成两个小区间处理。 然后是查询阶段,给定区间[x,y],需要计算k,使得2^k是小于等于y-x+1的最大值。根据引用[3],k是最大的满足条件的整数,可以通过对数计算或者位运算来找到。之后,区间被分成[x, x+2^k-1]和[y-2^k+1, y],这两个区间最大值分别是f[x][k]和f[y-2^k+1][k],取其中的较大者即为所求。 需要注意,用户提到的引用中,预处理数组的名称可能不同,比如引用[4]中的f数组是二维的,这可能对应到不同的变量命名。此外,预处理时需要计算log2的值,通常可以预先计算或者使用内置函数,但为了提高效率,可以预处理一个lg数组,存储每个长度的对数,如引用[1]中的lg数组。 在代码实现时,需要注意数组的索引是否从0开始或从1开始,这会影响具体的实现细节。比如,在Python中数组通常从0开始,而引用中的C++代码可能从1开始。因此,在编写代码时需要调整索引的起始位置,确保不会越界。 现在,我需要将以上步骤整理成结构化的回答,包括算法原理、实现步骤、代码示例以及相关问题。同时,需要遵循用户的格式要求,行内公式用$...$,独立公式用$$...$$,并且正确使用中文回答。此外,用户还要求生成相关问题,这些应该与ST表的实现、优化、应用等相关。 最后,检查是否符合系统级指令,比如是否所有数学公式正确使用LaTeX,是否有引用标记正确添加在引用的段落末尾。例如,在提到预处理时引用[2],查询步骤引用[3]等。</think>### ST表实现数列区间最大值的解决方法 #### 算法原理 ST表(Sparse Table)通过预处理和动态规划解决静态区间最值查询(RMQ)问题。其核心思想是:**任意区间的最值可通过两个预处理的子区间的最值计算得到**,且这两个子区间可以有重叠部分[^2]。预处理时构建二维数组$f[i][j]$,表示从位置$i$开始、长度为$2^j$的区间内的最大值。 #### 实现步骤 1. **预处理阶段** - **初始化**:$f[i][0] = a[i]$(单个元素的最大值为自身)[^1] - **递推填充**:对每个$j \geq 1$,计算: $$ f[i][j] = \max(f[i][j-1], f[i+2^{j-1}][j-1]) $$ 其中$i + 2^{j-1} \leq n$,保证区间不越界[^2] 2. **查询阶段** - 给定区间$[x,y]$,计算$k = \lfloor \log_2(y-x+1) \rfloor$ - 分解区间为两部分:$[x, x+2^k-1]$和$[y-2^k+1, y]$ - 结果:$\max(f[x][k], f[y-2^k+1][k])$[^3] #### 代码实现(Python示例) ```python import math def build_st(a): n = len(a) logn = math.floor(math.log2(n)) + 1 f = [[0]*logn for _ in range(n)] for i in range(n): f[i][0] = a[i] for j in range(1, logn): for i in range(n - (1 << j) + 1): f[i][j] = max(f[i][j-1], f[i + (1 << (j-1))][j-1]) return f def query_st(f, x, y): k = math.floor(math.log2(y - x + 1)) return max(f[x][k], f[y - (1 << k) + 1][k]) # 使用示例 arr = [3,1,4,5,2,9] st = build_st(arr) print(query_st(st, 1, 4)) # 输出区间[1,4]的最大值5 ``` #### 关键点说明 - **时间复杂度**:预处理$O(n \log n)$,查询$O(1)$ - **空间复杂度**:$O(n \log n)$ - **适用场景**:静态数据(无修改操作)的频繁区间查询[^4] - **优化技巧**:预计算对数表`lg[]`加速查询(如引用[1]中的`lg[y-x+1]`)
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值