思路:
这里首先定义几个数组:表示以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; }