题目大意:长度为n的非递减序列(ai<=aj,i<j),m次询问,询问[l,r]间出现次数最多的数所对应的次数(n,m<=100000)
思路分析:一开始用莫队+set的算法,但复杂度为m*sqrt(n)*log(n),果断给T掉了。。。。由于序列为非递减的,相同
的元素会连续出现,即每次询问为最长相同元素子序列的长度。可用线段树维护最长连续序列的长度,每
次更新时进行区间合并
代码
#include<iostream>
#include<stdio.h>
#include<string.h>
#include<math.h>
using namespace std;
int A[100010];
int ll[500010],rr[500010],sum[500010]; //ll,rr分别记录每个结点对应区间左右端最长连续序列的长度,sum记录该区间最长连续序列长度
void pushup(int root,int l,int r){
int mid=(l+r)/2,a;
sum[root]=max(sum[root*2],sum[root*2+1]);
ll[root]=ll[root*2];
rr[root]=rr[root*2+1];
if(A[mid]==A[mid+1]){ //横跨左右子区间时的情况
if(ll[root*2]==mid+1-l) ll[root]+=ll[root*2+1];
if(rr[root*2+1]==r-mid) rr[root]+=rr[root*2];
sum[root]=max(sum[root],rr[root*2]+ll[root*2+1]);
}
}
void update(int root,int l,int r,int k){
if(l==r){
sum[root]=ll[root]=rr[root]=1;
return;
}
int mid=(l+r)/2;
if(k<=mid) update(root*2,l,mid,k);
else update(root*2+1,mid+1,r,k);
pushup(root,l,r);
}
int q(int root,int l,int r,int a,int b){
int mid=(l+r)/2;
if(l==a&&r==b) return sum[root];
if(b<=mid) return q(root*2,l,mid,a,b);
else if(a>mid) return q(root*2+1,mid+1,r,a,b);
else{
int x,ans=max(q(root*2,l,mid,a,mid),q(root*2+1,mid+1,r,mid+1,b));
if(A[mid]==A[mid+1]){ //横跨时的情况
x=min(rr[root*2],mid-a+1)+min(ll[root*2+1],b-mid);
ans=max(ans,x);
}
return ans;
}
}
int main(){
int n,m,i,a,b;
while(scanf("%d",&n)!=EOF){
if(n==0) break;
scanf("%d",&m);
memset(ll,0,sizeof(ll));
memset(rr,0,sizeof(rr));
memset(sum,0,sizeof(sum));
for(i=1;i<=n;i++){
scanf("%d",&A[i]);
update(1,1,n,i);
}
while(m--){
scanf("%d%d",&a,&b);
printf("%d\n",q(1,1,n,a,b));
}
}
return 0;
}