思路:
这里首先定义几个数组:表示以i结尾的最长完美序列的开头位置,
为以2为底i的对数,f数组为每段区间l到r之间的st数组的最大值,
为
上次出现的位置。然后考虑如何更新st数组,首先,明显st数组是单调递增的,也就是
大于等于
,其次,它肯定大于等于
的上一次的出现位置。
考虑L到R这段区间,以R右侧为右端点的我们可以忽略不计(想一想,为什么),L左侧更不用说。因此我们只需要考虑以L到R这段区间为右端点即可。左端点<=L的右端点一定越靠又越好,我们可以二分找出左端点第一个大于等于L的位置x,长度就是x-l+1,之后,左端点在L到R区间内的,我们直接用表维护即可。
代码:
#include<cstdio>
#include<algorithm>
#include<cctype>
#define maxn 200007
using namespace std;
int lg[maxn],st[maxn],pre[maxn*21],f[maxn][21],n,m,p;
inline int qread() {
char c=getchar();int num=0,f=1;
for(;!isdigit(c);c=getchar()) if(c=='-') f=-1;
for(;isdigit(c);c=getchar()) num=num*10+c-'0';
return num*f;
}
inline int query(int l, int r) {
int k=lg[r-l+1];
return max(f[l][k],f[r-(1<<k)+1][k]);
}
int main() {
n=qread(),m=qread();
lg[0]=-1;
for(int i=1,x;i<=n;++i) {
x=qread()+maxn*10;
st[i]=max(st[i-1],pre[x]+1);
f[i][0]=i-st[i]+1;
pre[x]=i;
lg[i]=lg[i>>1]+1;
}
for(int j=1;j<=lg[n];++j)
for(int i=1;i+(1<<j)-1<=n;++i)
f[i][j]=max(f[i][j-1],f[i+(1<<(j-1))][j-1]); //st表。
for(int i=1,l,r;i<=m;++i) {
l=qread()+1,r=qread()+1;
if(st[r]<=l) {
printf("%d\n",r-l+1);
continue;
}
p=lower_bound(st+1,st+1+n,l)-st; //左端点不在l~r的。
int ans=0;
if(p-1>=l) ans=p-l; //p-1-l+1。
if(p<=r) ans=max(ans,query(p,r)); //在的,直接st表维护查询即可。
printf("%d\n",ans);
}
return 0;
} 
京公网安备 11010502036488号