注意到数列是非递减的,这代表相等的数字一定是挨在一起的。
用a[i]表示数字i所在的段的长度。
用left,right数组维护每个数字所在段左右边界位置。
这样对于查询s~t,right[s]+1~left[t]-1这个子区间全都是完整的段,对这个子区间做RMQ,得到最长的段长度。然后再考虑这个子区间两个边界到s,t的距离。这两个距离也可能是答案。这三个答案的最大值就是最终答案。
要特判s和t相等,以及right[s]+1>=left[t]-1不合法。
RMQ用ST算法做,预处理nlogn,查询O(1)。
思路是用c[i][j]表示从位置i开始,到i+(1<<j)-1这一段的最大值。
递推方程是
c[j][i]=max(c[j][i-1],c[j+(1<<(i-1))][i-1]);
即当前段最大值等于两个子段最大值中最大的。
完整ST算法代码在下面。
代码:
//
// main.cpp
// 11235 Frequent Values
//
// Created by Baoli1100 on 15/3/13.
// Copyright (c) 2015年 Baoli1100. All rights reserved.
//
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
int N,Q;
int c[100005][20];
int a[100005];
int p[100005];
int Left[100005];
int Right[100005];
void init(){
for(int i=0;i<N;i++){
c[i][0]=a[i];
}
for(int i=1;(1<<i)<=N;i++){
for(int j=0;j+(1<<i)<=N;j++){
c[j][i]=max(c[j][i-1],c[j+(1<<(i-1))][i-1]);
}
}
}
int Query(int s,int t){
int len=t-s+1;
int k=0;
while((1<<(k+1))<=len){
k++;
}
return max(c[s][k],c[t-(1<<k)+1][k]);
}
int main(){
while(~scanf("%d",&N)){
if(!N) break;
scanf("%d",&Q);
for(int i=0;i<N;i++){
scanf("%d",&p[i]);
}
memset(a,0,sizeof(a));
int pos=0;Left[0]=0;
a[0]=1;
for(int i=1;i<N;i++){
if(p[i]==p[i-1]){
a[i]=a[i-1]+1;
}
else {
pos=i;
a[i]=1;
}
Left[i]=pos;
}
pos=N-1;Right[N-1]=N-1;
for(int i=N-2;i>=0;i--){
if(p[i]==p[i+1]) a[i]=a[i+1];
else pos=i;
Right[i]=pos;
}
init();
for(int i=0;i<Q;i++){
int s,t;
scanf("%d%d",&s,&t);
s--;t--;
int res=0;
if(p[s]==p[t]) res=t-s+1;
else{
if(Right[s]+1<Left[t]-1)res=Query(Right[s]+1,Left[t]-1);
res=max(res,Right[s]-s+1);
res=max(res,t-Left[t]+1);
}
printf("%d\n",res);
}
}
}