RMQ问题第二题,还是用线段树写的,DP很渣ST算法看不懂啊~~
题目大意:
给出依次不下降的n个数,求某个区间内出现次数最多的数字的个数。
解题思路:
这题用线段树写的话如果想不出来就卡死,想出来了真的很简单~~
对于每一个区间来说,会有中间的整个的数块,和两端的小数块。中间的用线段树查询就行,两边的用二分来查找。代码中有注释。
下面是代码:
#include <stdio.h>
#include <string.h>
const int Max=100005;
int n,q;
int num[Max],sum[Max],node[Max<<2];
int max(int a,int b)
{
if(a<b)a=b;
return a;
}
void PushUp(int tr)
{
node[tr]=max(node[tr<<1],node[tr<<1|1]);
}
void Build(int l,int r,int tr)
{
if(l==r)
{
node[tr]=num[l];
return ;
}
int m=(l+r)>>1;
Build(l,m,tr<<1);
Build(m+1,r,tr<<1|1);
PushUp(tr);
}
int Findnum(int key ,int l,int r)
{
int m;
while(l<=r)
{
m=(l+r)>>1;
if(sum[m]>key)
{
r=m-1;
}
else if(sum[m]<key)
{
l=m+1;
}
else break;
}
if(sum[m-1]>=key)
{
return m-1;
}
else if(sum[m]>=key)
{
return m;
}
else
{
return m+1;
}
}
int query(int L,int R ,int l , int r , int tr )
{
if(L<=l&&r<=R)
{
return node[tr];
}
int m=(l+r)>>1;
int ans=0;
if(L<=m)ans=query(L,R,l,m,tr<<1);
if(m<R)ans=max(ans,query(L,R,m+1,r,tr<<1|1));
return ans;
}
int main()
{
while(scanf("%d",&n),n)
{
memset(num,0,sizeof(num));
memset(node,0,sizeof(node));
memset(sum,0,sizeof(sum));
scanf("%d",&q);
int tag,chack,cnt=1,a,b;
scanf("%d",&chack);
num[cnt]=1;
sum[0]=0;
for(int i=1;i<n;i++) //记录一共有几个不同的数字 cnt计数
{
scanf("%d",&tag);
if(chack==tag)
{
num[cnt]++; //num数组记录每个数字出现了几次
}
else
{
sum[cnt]=sum[cnt-1]+num[cnt]; //sum数组记录从第一种数开始到第i种数一共有多少个
chack=tag;
cnt++;
num[cnt]=1;
}
}
sum[cnt]=sum[cnt-1]+num[cnt];
Build(1,cnt,1); //对于数的出现次数计数
for(int i=0;i<q;i++)
{
scanf("%d%d",&a,&b);
int x=Findnum(a,1,cnt); //二分查找出第a个数是第几种数
int y=Findnum(b,1,cnt);
int ans=sum[x]-a+1; //计算出在a到b这个区间中第x种数有几个
if(x==y) //当x与y相等时,代表着区间a到b内只有一种数
{
printf("%d\n",b-a+1);
continue;
}
if(x+1<=y-1)
{
ans=max(ans,query(x+1,y-1,1,cnt,1));
}
ans=max(ans,num[y]-sum[y]+b);
printf("%d\n",ans);
}
}
return 0;
}