题意:给你一个长度为n的非降序排列的序列,并提出q个询问区间,i,j,请你求出[i,j]中出现次数最多的数出现的次数
这道题是最为经典的RMQ问题,我们注意到整个数组是非降序排列的,因此相同的元素肯定都聚集在一起,这样我们就可以把整个数组进行游程编码
比如 -1 1 1 2 2 2 4就可以编码成(-1,1)(1,2)(2,3)(4,1)其中(a,b)表示b个连续的a因此用cnt[i]记录第i组的元素个数,num[p],Left[p],Right[p]分别表示位置p所在的组的编号,和左右端点位置,因此每次查询[i,j]的结果为一下三个部分的最大值:
1.从i到Right[i]的元素个数
2.从j到Left[j]的元素个数
3.从第num[i]+1段到num[j]-1段的cnt的最大值
可以看到,1,2部分都可以直接算出来,第3个部分就要用到RMQ来计算了,不过要注意一下特殊情况的处理
#include<cstdio>
#include<iostream>
#include<cstring>
using namespace std;
const int maxn=100005,inf=1e9;
inline void _read(int &x){
char t=getchar();bool sign=true;
while(t<'0'||t>'9')
{if(t=='-')sign=false;t=getchar();}
for(x=0;t>='0'&&t<='9';t=getchar())x=x*10+t-'0';
if(!sign)x=-x;
}
int n,q,r[maxn],l[maxn],cnt[maxn],num[maxn];
int Left[maxn],Right[maxn];
int f[maxn][20],type=-1,s[3];
void insert(){
int i,j;
for(i=0;i<type;i++)f[i][0]=cnt[i];
for(j=1;(1<<j)<=type;j++)
for(i=0;i+(1<<j)<=type;i++)
f[i][j]=max(f[i][j-1],f[i+(1<<(j-1))][j-1]);
}
int rmq(int ls,int rs){
if(ls==rs)return cnt[ls];
if(rs<ls)return 0;
int k=0;
while((1<<(k+1))<=rs-ls+1)k++;
return max(f[ls][k],f[rs-(1<<k)+1][k]);
}
void clear(){
memset(f,0,sizeof(f));
memset(cnt,0,sizeof(cnt));
}
int main(){
while(scanf("%d",&n)&&n){
_read(q);
int i,j;
s[1]=inf;
for(i=1;i<=n;i++){
_read(s[i&1]);
if(s[i&1^1]!=s[i&1]){
r[type]=i-1;
l[++type]=i;
}
num[i]=type;
cnt[type]++;
}
r[type]=n;
type++;
insert();
for(i=1;i<=n;i++){
int t=num[i];
Left[i]=l[t];
Right[i]=r[t];
}
while(q--){
_read(i);_read(j);
if(num[i]==num[j])printf("%d\n",j-i+1);
else {
int ans=rmq(num[i]+1,num[j]-1);
ans=max(ans,max(Right[i]-i+1,j-Left[j]+1));
printf("%d\n",ans);
}
}
}
}