RMQ poj3368

题目链接

题目大意

  有一个数字串长度为n,输入顺序为非递减,给出一个区间[L,R],要求算出区间中某个出现次数最多的数,答案为它出现的次数。
  1. N< 105 , 区间数cas< 105
  2. 多组测试,以0结尾
 这里写图片描述

解题思路

  因为数组是非递减序列,所以可以将数组分段。(也叫游程编码,Run Length Encoding RLE)
  1.扫描一遍数组,求如下:
   - count[ i ] : 表示第 i 段中数字出现的次数总和
   - num[ p ] : 表示位置P所在段的编号
   - Left [ p ] , Right[ p ] : 分别表示P所在段的左右端点的位置(这里的位置都是指的在原数组中的位置)
  2. 根据count 数组我们可以建立RMQ,每一个数据段都有一个相应出现的次数。
  3. 对于给出的区间 [ L, R],按照如下进行求解:
   -num[L] = num[R] : 表示给出的区间在一个段落,那么ans = R - L +1;
   - 否则 结果至少是L到L所在段的最右端距离 和 R所在段的最左端到R的距离 的最大值,ans = max(Right[L]-L+1, R-Left[R]+1);
   - 如果 num[R] > num[L]+1 : 那么说明这个区间中至少有3个段,所以ans = max(ans, RMQ(num[L]+1, num[R]-1)), 在RMQ中找出中间段落的最大值区间为:num[L]+1 到 num[R]-1.

代码献上

#include <iostream>
#include <cstdio>
#include <cmath>
#include <cstring>
#include <algorithm>

using namespace std;

const int MAX_N = 100000;

int a[MAX_N+1],coun[MAX_N+1],num[MAX_N+1];
int Left[MAX_N+1],Right[MAX_N+1],d[MAX_N][20];

//这里采用Sparse-Table 基于稀疏表的RMQ
void RMQ_init(int n)
{
    for(int i = 0; i<n; i++) d[i][0] = coun[i];
    for(int j = 1; (1<<j)<=n; j++)
        for(int i = 0; i+(1<<j) -1<n; i++)
          d[i][j] = max(d[i][j-1], d[i+(1<<j-1)][j-1]);
}

int RMQ(int L, int R)
{
    int k = 0;
    while((1<<(k+1)) <= R-L+1) k++;
    return max(d[L][k],d[R-(1<<k)+1][k]);
}

int main()
{
    int n,cas;
    while(1)
    {
        scanf("%d",&n);
        if(n == 0) break;
        scanf("%d",&cas);
        for(int i = 0; i<n; i++)
            scanf("%d",&a[i]);

        int total = 0;
        //一遍扫描
        for(int i = 0; i<n; )
        {
            int s = upper_bound(a,a+n,a[i]) - lower_bound(a,a+n,a[i]);
            coun[total] = s;
            for(int j = i ; j<i+s; j++){
                num[j] = total;
                Left[j] = i;
                Right[j] = i+s-1;
            }
            i = i+s;
            total++;
        }
        //建立RMQ
        RMQ_init(total);

        //求解
        for(int i = 0; i<cas; i++){
            int l,r;
            scanf("%d%d",&l,&r);
            l = l-1;
            r = r-1;
            int ans;
            if(num[l] == num[r]) ans = r-l+1;
            else ans = max(Right[l]-l+1, r-Left[r]+1);
            if(num[r]>num[l]+1) ans = max(ans, RMQ(num[l]+1, num[r]-1));
            printf("%d\n",ans);
        }
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值