2017图灵杯问题E 简单的RMQ(RMQ)

题目描述

给定一个数组,其中的元素满足非递减顺序。任意给定一个区间[i,j],求其中某个元素重复出现的最大次数。

输入

多组数据输入。每组数据的第一行包含两个整数n和q(1<=n,q<=100000),下一行包含n个整数a1,...,an(-100000<=ai<=100000,i∈{1,...,n}),用空格分隔,数列是升序的(ai<=ai+1)。接下来的q行,每行包含两个整数i和j(1<=i<=j<=n),表示给定区间[i,j]。
输入结束于0(自成一行)。

输出

对输入的q个区间,每个区间输出一个整数表示该区间内重复最多的元素出现的次数,用换行分隔。

样例输入

10 3
-1 -1 1 1 1 1 3 10 10 10
2 3
1 10
5 10
0

样例输出

1
4
3

思路:前提非降序列,即所有相等的数聚在一起,所以我们可以编码一个二元组,如-1,1,1,2,2,2,4就可以编码成(-1,1),(1,2),(2,3),(4,1),其中(a,b)表示a连续出现了b次;num[i]表示第i个位置的数在哪一段,count[i]代表第i段连续的长度,Left[i],Right[i]分别表示第i段的左右端点位置;这样的话每次查询(L, R)就只要计算(Right[L]-L+1),(R-Left[R]+1)和RMQ(num[L]+1,num[R]-1)(RMQ是对count数组进行取件查询的结果)这三个值的最大值就可以了;

RMQ链接点击打开链接


代码:

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;

typedef long long ll;
const int MaxN=100000+10;
int Left[MaxN],Right[MaxN],count[MaxN],num[MaxN];
int nn,q,a,last,tot;

const int INF=0x3f3f3f3f;   
  
//存储线段树的全局数组;  
int n,dat[2*MaxN-1];  
  
//初始化  
void init(int n_)  
{  
     //为计算方便,元素个数扩大为2的幂;  
     n=1;  
     while(n<n_)  n*=2;  
     for(int i=0;i<2*n-1;i++)  
          dat[i]=-INF;  
}  
  
//n_个元素的初始化:for(int k=0;k<n_;k++)   update(k,a[k]);  
//把第k个值(从0开始)更新为a;  
void update(int k,int a)  
{  
       k+=n-1;  
       dat[k]=a;  
       //向上更新;  
       while(k>0)  
       {  
             k=(k-1)/2;  
             dat[k]=max(dat[2*k+1],dat[2*k+2]);  
        }  
}  
  
//求[a,b)的最小值;  
//后面的参数是为了计算方便传入的;  
//k是节点的编号,l,r表示这个节点对应的是[l,r)区间;  
//外部调用时,用query(a,b,0,0,n);  
int query(int a,int b,int k,int l,int r)  
{  
      //如果[a,b)和[l,r)不相交,则返回-INF;  
      if(r<=a||b<=l) return -INF;  
        
      //如果[a,b)完全包含[l,r),则返回当前节点的值;  
      if(a<=l&&r<=b) return dat[k];  
      else{  
            //否则返回两个儿子中值的较大者;  
            int vl=query(a,b,k*2+1,l,(l+r)/2);  
            int vr=query(a,b,k*2+2,(l+r)/2,r);  
            return max(vl,vr);  
       }  
}  


int main(){
    while(~scanf("%d", &nn) && nn)
    {
        scanf("%d", &q);
	    tot = 0;
        for(int i=1;i<=nn;i++)
        {
           scanf("%d", &a);
           if(i==1) 
		   {    
			   last=a;  
			   Left[tot]=1; 
			}
           if(last==a) 
		   { 
		       num[i]=tot; 
			   count[tot]++; 
			   Right[tot]++; 
			}
           else 
		   { 
		       num[i]=++tot; 
			   count[tot]++; 
			   Left[tot]=Right[tot]=i; 
			   last=a; 
			}
        }
		init(tot+1);
		for(int k=0;k<=tot;k++)
		    update(k,count[k]);
		int l, r;
        for(int i=0;i<q;i++)
       {
           scanf("%d%d", &l, &r);
           if(num[l] == num[r]) { printf("%d\n", r-l+1);  continue; }
           printf("%d\n", max( query(num[l]+1,num[r]-1+1,0,0,n), max( Right[num[l]]-l+1, r-Left[num[r]]+1 ) ) );
       }
    }
    return 0;
}


  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值