Description
给你一个由n个数字组成的非降序序列,有m次询问,每次询问区间[l,r]之间出现最多的数字出现的次数
Input
第一行两个整数n和m分别表示序列长度和查询次数,第二行n个整数表示该序列,之后m行每行两个整数l和r表示查询区间
Output
对于每次查询,输出区间[l,r]中出现次数最多的数字的次数
Sample Input
10 3
-1 -1 1 1 1 1 3 10 10 10
2 3
1 10
5 10
0
Sample Output
1
4
3
Solution
该题只涉及查询不涉及修改选用ST算法
我们用数组counts,按顺序统计一个数字连续·出现的次数,这个数字保存在value中(本题不需要保存,如果询问是哪个数则需要),然后用一个结构统计一个下标所在的区间的左右范围,以及当前下标的数字在counts中属于哪个位置,最后对counts进行RMQ初始化,查询区间最大值,
对于l,r的询问
我们ans=max(从l到l所在段的结束处元素数量,从r到r开始出的元素数量,位于l和r之间的最大值(rmq查询))
注意l和r位于一个区间的情况,已经找l,r中间的区间的时候,可能会出现最后L>R的情况。
Code
#include<cstdio>
#include<cstring>
#include<iostream>
#include<cmath>
#include<algorithm>
using namespace std;
#define maxn 100010
struct node
{
int l,r,p;
}data[maxn];
int n,m,p;
int cnt[maxn];
int value[maxn];
int dp[maxn][60];
void init()
{
for(int i=0;i<n;i++)
dp[i][0]=cnt[i];
int k=(int)(log(n*1.0)/log(2.0));
for(int j=1;j<=k;j++)
for(int i=1;i+(1<<j)<=p+1;i++)
dp[i][j]=max(dp[i][j-1],dp[i+(1<<(j-1))][j-1]);
}
int rmp(int i,int j)
{
int m=(int)((log((double)(j-i+1))/log(2.0)));
int x=max(dp[i][m],dp[j-(1<<m)+1][m]);
return x;
}
int main()
{
while(scanf("%d",&n),n)
{
scanf("%d",&m);
int l=0,r=0,v=0,t;
p=1;
for(int i=1;i<=n;i++)
{
scanf("%d",&t);
if(t!=v&&l!=0)//按顺序统计一个数字连续出现的次数
{
cnt[p]=r-l+1;
value[p]=t;//这个数字保存在value中
for(int j=l;j<=r;j++)//用一个结构统计一个下标所在的区间的左右范围
data[j].l=l,data[j].r=r,data[j].p=p;
p++;r++;l=r;v=t;
}
else
r++;
if(l==0)//第一段连续的数字
{l=1;v=t;}
}
cnt[p]=r-l+1;//最后一段连续的数字
value[p]=v;
for(int j=l;j<=r;j++)
data[j].l=l,data[j].r=r,data[j].p=p;
init();//初始化RMQ
while(m--)
{
scanf("%d%d",&l,&r);
if(data[l].p==data[r].p)//l和r处于同一段
printf("%d\n",r-l+1);
else
{
int ll=data[l].p+1;
int rr=data[r].p-1;
int ans=data[l].r-l+1;//l到l所在段结束处的元素数量
ans=max(ans,r-data[r].l+1);//r到r所在段起始处的元素数量
if(ll<=rr)//l和r之间还有其他段
ans=max(ans,rmp(ll,rr));
printf("%d\n",ans);
}
}
}
return 0;
}