思路:首先应该想到dp,我们约定pre[i] 为i号位为结尾的数的起始位置,f[i]为i号位为结尾的最长序列长度,不难得到 pre[i]=max(pre[i-1],last[a[i]]+1) f[i]=i-pre[i]+1
对于一个访问区间有两种序列长度:1.整个序列被包含在区间内 2.整个序列被不完全包含在区间内
因为上面的式子可以得到pre[i] 为一个单调不降序列,于是我们可以用二分来寻找分界点,那么第二种序列长度就为pos-l+1,下面就是处理第一种答案了,这里运用RMQ算法来加速区间最大值查询
注意:a[i]有负值,需要将所有元素全加上1e6来保证数去全为正以防爆数组
Code
#include<bits/stdc++.h>
#define re register
#define int long long
#define inl inline
using namespace std;
int read(){
int sum=0,f=1;char c=getchar();
while(!isdigit(c)){if(c=='-') f=-1;c=getchar();}
while(isdigit(c)){sum=(sum<<3)+(sum<<1)+(c^48);c=getchar();}
return sum*f;
}
const int N=2e5+10;
const int M=1e6+10;
int n,m,a[N],pre[N],f[N][30],lg[N],last[M+M+1000];
inl void init(){
lg[0]=-1;
for(re int i=1;i<=n;i++) lg[i]=lg[i>>1]+1;
}
inl int find(int l,int r){
int now=l;
int x=l,y=r;
while(x<=y){
int mid=x+y>>1;
if(pre[mid]<l) now=mid,x=mid+1;
else y=mid-1;
}
return now;
}
inl int query(int l,int r){
int k=lg[r-l+1];
return max(f[l][k],f[r-(1<<k)+1][k]);
}
signed main(){
n=read(),m=read();
init();
for(re int i=1;i<=n;i++) a[i]=read();
pre[1]=1;last[a[1]+M]=1;
f[1][0]=1;
for(re int i=2;i<=n;i++){
pre[i]=max(pre[i-1],last[a[i]+M]+1);
last[a[i]+M]=i;f[i][0]=i-pre[i]+1;
}
for(re int j=1;j<=lg[n];j++){
for(re int i=1;i+(1<<j)-1<=n;i++){
f[i][j]=max(f[i][j-1],f[i+(1<<(j-1))][j-1]);
}
}
for(re int i=1;i<=m;i++){
int l=read(),r=read();
l++,r++;
int pos=find(l,r);
int ans=pos-l+1;
if(pos<r) ans=max(ans,query(pos+1,r));
printf("%lld\n",ans);
}
return 0;
}