题目描述:
给出一个有N个数的序列,编号0 - N - 1。进行Q次查询,查询编号i至j的所有数中,最大的数是多少。
例如: 1 7 6 3 1。i = 1, j = 3,对应的数为7 6 3,最大的数为7。(该问题也被称为RMQ问题)
收起
输入
第1行:1个数N,表示序列的长度。(2 <= N <= 10000) 第2 - N + 1行:每行1个数,对应序列中的元素。(0 <= S[i] <= 10^9) 第N + 2行:1个数Q,表示查询的数量。(2 <= Q <= 10000) 第N + 3 - N + Q + 2行:每行2个数,对应查询的起始编号i和结束编号j。(0 <= i <= j <= N - 1)
输出
共Q行,对应每一个查询区间的最大值。
输入样例
5 1 7 6 3 1 3 0 1 1 3 3 4
输出样例
7 7 3
题解:
假如用暴力方式求解的话,共 TT 次查询,每次查询区间 (i,j)(i,j) ,暴力是 O(n)O(n) 的,因此总的复杂度为 O(T×n)O(T×n) 。是无法通过评测的。
区间最大不能像前缀和一样,用前缀最大来处理。那么如何来做预处理呢?
一个表不够的话,我们就用多个表来做。假如数组长度为 nn ,我们用 log(n)log(n) 个表来做预处理。
St[i][k]St[i][k] 用来记录以第 ii 个数为开始,长度为 2k2k 的区间内的最大值。
所以有:
St[i][k]=Max(St[i][k−1],St[i+2k−1][k−1])St[i][k]=Max(St[i][k−1],St[i+2k−1][k−1])
这样我们可以通过一个 O(nlog(n))O(nlog(n)) 的预处理,得到这 log(n)log(n) 个表的所有值。
例如: {9,13,3,5,21,17,1,11}{9,13,3,5,21,17,1,11} 。
St[i] | |
---|---|
St[0]St[0] | {9,13,3,5,21,17,1,11}{9,13,3,5,21,17,1,11} |
St[1]St[1] | {13,13,5,21,21,17,11}{13,13,5,21,21,17,11} |
St[2]St[2] | {13,21,21,21,21}{13,21,21,21,21} |
St[3]St[3] | {21}{21} |
有了这个表后,任意一段区间 (i,j)(i,j) 的最值,可以拆分为 22 个值 St[i][k],St[j−2k−1][k]St[i][k],St[j−2k−1][k] 中取较大的。
前一段区间 (i,i+2k−1)(i,i+2k−1) 中的最大值为: St[i][k]St[i][k] (以 ii 开头)
后一段区间 (j−2k+1,j)(j−2k+1,j) 中的最大值为: St[j−2k−1][k]St[j−2k−1][k] (以 jj 结尾)
且 22 段区间的并集,刚好覆盖 (i,j)(i,j) 。
这样,区间最大的查询,可以 O(1)O(1) 完成。
例如:查询区间为 (4,17)(4,17) ,区间总长度为 1414 ,相当于询问 (4,11)(4,11) 和 (10,17)(10,17) 这两个区间(长度均为 88 )最大值的最大值。而这 22 段区间的最大值都已经通过预处理提前求出来了,我们从 StSt 中找到对应的长度 88 ,再分别找到 4,104,10 两个位置具体的值,就可以 O(1)O(1) 求出区间 (4,17)(4,17) 的最大值了。
以上就是 RMQRMQ 问题的 StSt 表解法。
预处理部分 O(nlog(n))O(nlog(n)) ,单次查询 O(1)O(1) ,总的复杂度为 O(nlog(n)+T)O(nlog(n)+T) 。
#include <iostream>
#include <cstring>
#include <stdlib.h>
#include <stdio.h>
#include <math.h>
#include <algorithm>
#define pi 3.1415926535898
#define e 2.718281828459
using namespace std;
typedef long long ll;
int main()
{
int n;
cin>>n;
ll a[n];
for(int i=0;i<n;i++)
{
cin>>a[i];
}
int q;
cin>>q;
while(q--)
{
int i,j,max=0;
cin>>i>>j;
for(int ii=i;ii<=j;ii++)
{
if(max<=a[ii])
max=a[ii];
}
cout<<max<<endl;
max=0;
}
return 0;
}