题目速递:https://onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&problem=2176
题意:在一个长度为n、不严格递增的序列中,给定q个询问,每次询问某一区间中出现次数最多的数字,输出那个数字在区间中的出现次数。
题解:使用线段树维护区间连续段最大的长度。
首先要注意,该序列是不严格递增的,这是个很重要的前提。
区间合并时,应考虑左区间最右数字和右区间最左数字是否相同:
- 不相同的话,合并后的区间连续段最长长度就在两个子区间的连续段最长长度中取max就可;
- 相同时,则还需要考虑区间中间那个数字在区间中的连续段长度。
还有另外一种方法——莫队,具体见:训练3.22(UVA 11235 Frequent values莫队离线解法)
线段树解法的代码如下:
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<vector>
using namespace std;
#define ll long long
const int z=100000;
const int maxn=2e6+6;
struct Node{
int mc,mt,lc,rc,l,r;
}T[4*maxn];
int a[maxn];
int l[maxn],r[maxn];
Node mergy(Node a,Node b){
Node ans=(Node){0,0,a.lc,b.rc,a.l,b.r};
if(a.mt>b.mt)ans.mt=a.mt,ans.mc=a.mc;
else
ans.mt=b.mt,ans.mc=b.mc;
if(a.rc!=b.lc)return ans;
int c=a.rc;
int tmp=min(b.r,r[c+z])-max(a.l,l[c+z])+1;
if(ans.mt<tmp){
ans.mt=tmp;
ans.mc=c;
}
return ans;
}
void build(int l,int r,int x){
if(l==r){
T[x]=(Node){a[l],1,a[l],a[l],l,l};
return;
}
int mid=(l+r)>>1;
build(l,mid,2*x);
build(mid+1,r,2*x+1);
T[x]=mergy(T[2*x],T[2*x+1]);
}
Node query(int L,int R,int l,int r,int x){
if(L<=l&&r<=R)return T[x];
int mid=(l+r)>>1;
Node x1,x2;
x1=x2=(Node){0,0,0,0,0,0};
if(L<=mid)x1=query(L,R,l,mid,2*x);
if(R>mid)x2=query(L,R,mid+1,r,2*x+1);
if(x1.mt&&x2.mt)return mergy(x1,x2);
if(x1.mt)return x1;
if(x2.mt)return x2;
}
int main(){
int n,q;
while(cin>>n&&n){
cin>>q;
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
if(a[i]!=a[i-1]||i==1)l[a[i]+z]=i;
if(i!=1&&a[i]!=a[i-1])r[a[i-1]+z]=i-1;
}
r[a[n]+z]=n;
build(1,n,1);
int l,r;
for(int i=1;i<=q;i++){
scanf("%d%d",&l,&r);
Node ans=query(l,r,1,n,1);
printf("%d\n",ans.mt);
}
}
}