题目
题意
一个非降序序列,有若干查询,每次查询一个区间中重复次数最多的数字的个数。
思路
因为是非降序的,所以可以从头遍历把每个相同的数字划为一个集合,用p[i]表示ai划分到了哪个块里面,同时还可以记录每个集合的左右边界,同时还可以获得每个集合中数字的个数。可以把这些个数处理成ST表。
对于查询的区间,有三种可能
(1)该区间只有一个集合
那就直接右边界-左边界+1
(2)该区间有两个集合
找出左边界所在集合,在该区间的个数,以及右边界所在集合,在该区间的个数,比较二者大小
(3)该区间有两个以上的集合
先重复(2),找出左右边界集合的最大值之后,在通过ST表查询中间这些集合的最大值,然后把二者比较,就可得出最终最大值
代码
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<cstdio>
using namespace std;
const int maxn=1e5+5;
const int inf=0x3f3f3f3f;
typedef long long ll;
typedef pair<int,int> PII;
int F[maxn][20];
int a[maxn];
int n,q;
int Left[maxn],Right[maxn],pos[maxn];
int p;
void ST()
{
memset(F,0,sizeof(F));
for(int i=1;i<=p;i++)
F[i][0]=Right[i]-Left[i]+1;
int k=log2(p);
for(int j=1;j<=k;j++)
{
for(int i=1;i<=p-(1<<j)+1;i++)
F[i][j]=max(F[i][j-1],F[i+(1<<(j-1))][j-1]);
}
}
int query(int l,int r)
{
int k;
k=log2(r-l+1);
return max(F[l][k],F[r-(1<<k)+1][k]);
}
int main()
{
while(~scanf("%d %d",&n,&q)&&n)
{
memset(Left,0,sizeof(Left));
memset(Right,0,sizeof(Right));
memset(pos,0,sizeof(pos));
p=0;
for(int i=1;i<=n;i++)
scanf("%d",&a[i]);
a[0]=inf;
for(int i=1;i<=n;i++)
{
if(a[i]!=a[i-1])
{
p++;
pos[i]=p;
Right[p]=Left[p]=i;
}
else
{
pos[i]=p;
Right[p]++;
}
}
ST();
int from,to;
while(q--)
{
scanf("%d %d",&from,&to);
int ans;
if(pos[from]==pos[to])
ans=to-from+1;
else if(pos[to]-pos[from]==1)
ans=max(Right[pos[from]]-from+1,to-Left[pos[to]]+1);
else
{
ans=max(Right[pos[from]]-from+1,to-Left[pos[to]]+1);
ans=max(ans,query(pos[from]+1,pos[to]-1));
}
cout<<ans<<endl;
}
}
}