51nod 1174 区间中最大的数

这篇博客介绍了如何解决区间最大值查询问题(RMQ问题),通过使用St表进行预处理,实现O(nlog(n))的预处理时间和O(1)的单次查询时间,总复杂度为O(nlog(n)+T)。
摘要由CSDN通过智能技术生成

题目描述:

给出一个有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;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值