UVA 11235 - Frequent values(预处理+RMQ)
题目链接 https://cn.vjudge.net/problem/UVA-11235
【题意】
给定一个非降序排列的整数数组a(1),a(2)…a(n),你的任务是对于一系列询问(i,j),回答a(i),a(i+1)…a(j)中出现次数最多的值所出现的次数
【输入格式】
多组输入,第一行为两个整数n,q(1<=n,q<=1e5),第二行包含n个非降序排列的整数a(1),a(2)…a(n)(-1e5<=a(i)<=1e5)以下q行每行两个数i,j(1<=i<=j<=n),当n=0时输入结束
【输出格式】
对于每组查询输出出现最多的次数值
【思路】
预处理+RMQ,预处理时,注意到所有的元素是非降序的,所有相等的元素会聚集在一起,可以把整个数组进行游程编码,将若干相同元素看作一段,v[i],cnt[i]表示第i段的元素值为v[i],共有cnt[i]个这样的元素,同时对于原序列,我们还可以预处理出num[p],每个位置对应的段编号;left[i],right[i],每个段的左右端点在原序列中的位置。这样对于每次查询(le,ri),答案就从三个部分找,一个是从le到le所在段的结束,一个是ri所在段的开始到ri,还有就是从num[le]+1到num[ri]-1段中cnt的最大值,注意当ri和le在同一段中时,答案就是ri-le+1
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=1e5+50;
int n,m,q;
int a[maxn<<1];
int num[maxn],v[maxn],cnt[maxn];
int L[maxn],R[maxn];
int dp[maxn][50];
void RMQ_init(){
for(int i=0;i<m;++i) dp[i][0]=cnt[i];
for(int j=1;(1<<j)<=m;++j){
for(int i=0;i+(1<<j)-1<m;++i){
dp[i][j]=max(dp[i][j-1],dp[i+(1<<(j-1))][j-1]);
}
}
}
int RMQ(int le,int ri){
int k=0;
while((1<<(k+1))<=ri-le+1) ++k;
return max(dp[le][k],dp[ri-(1<<k)+1][k]);
}
int main(){
while(scanf("%d",&n)==1 && n){
m=0;
memset(a,0,sizeof(a));
scanf("%d",&q);
for(int i=0;i<n;++i){
int x;
scanf("%d",&x);
x+=1e5;
++a[x];
}
int s=0;
for(int i=0;i<=2e5;++i){
if(a[i]==0) continue;
v[m]=i;
cnt[m]=a[i];
L[m]=s;
R[m]=s+a[i]-1;
for(int j=L[m];j<=R[m];++j) num[j]=m;
s+=a[i];
++m;
}
RMQ_init();
while(q--){
int le,ri;
scanf("%d%d",&le,&ri);
--le,--ri;
if(num[le]==num[ri]){
printf("%d\n",ri-le+1);
}
else{
int ans=max(R[num[le]]-le+1,ri-L[num[ri]]+1);
if(num[le]+1<=num[ri]-1){
ans=max(ans,RMQ(num[le]+1,num[ri]-1));
}
printf("%d\n",ans);
}
}
}
return 0;
}