题目大意
给出一个长度为
n
的排列,有
1≤n,m≤50000
题目分析
这题考察我们对莫队算法的灵活运用。
显然使用莫队算法加上线段树维护最大连续子段可以做到
O(nn−−√log2n)
,然而这个算法太慢了。
我们考虑如果所有操作都是插入操作,那么答案是不下降的,每次操作我们可以
O(1)
维护联通块左右端点进行更新。问题是这题里面会有删除操作。
考虑对莫队算法进行小的修改,分块依据依然不变,然而在处理每一块的询问时,我们始终让左指针先位于块位,右指针和普通莫队一样递增地扫,处理询问时,我们可以让左指针向左调整,显然这些操作都是插入操作,可以
O(1)
处理。
问题是我们同一块内询问左段不一定单调不上升,因此我们需要还原我们的插入操作。乍一看好像还是要打删除,其实不然,答案我们可以直接在处理询问之前存好,暴力赋值还原,而对于联通块的维护,我们可以记录每次合并的块的左右端点和中间点,然后
O(1)
还原(这个自己思考吧,不会看代码)。
然后还要注意如果左指针在右指针右边,需要特殊处理。我是强制限定当左指针在右指针左端(或重合)时才执行插入,这样就可以简洁明了地处理所有情况了。
时间复杂度
O(nn−−√)
。
代码实现
#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdio>
#include <cctype>
#include <cmath>
using namespace std;
int read()
{
int x=0,f=1;
char ch=getchar();
while (!isdigit(ch)) f=ch=='-'?-1:f,ch=getchar();
while (isdigit(ch)) x=x*10+ch-'0',ch=getchar();
return x*f;
}
const int N=50050;
const int M=50050;
int lp[N],rp[N];
bool mark[N];
int ans[M];
int n,m,s;
int p[N];
struct Q
{
int l,r,id;
}q[M];
bool operator<(Q x,Q y){return (x.l-1)/s<(y.l-1)/s||(x.l-1)/s==(y.l-1)/s&&x.r<y.r;}
int did[N][3];
int cnt;
int now,lcur,rcur;
void change(int x,bool flag)
{
if (lcur<=rcur)
{
int delta=0,y;
x=p[x];
mark[x]=true,lp[x]=rp[x]=x;
if (flag) cnt++,did[cnt][0]=did[cnt][1]=did[cnt][2]=x;
if (x&&mark[x-1]) delta+=x-lp[x-1],lp[x]=lp[x-1],rp[lp[x-1]]=x,flag?did[cnt][0]=lp[x-1]:0;
if (x+1<=n&&mark[x+1]) y=lp[x],delta+=rp[x+1]-x,rp[y]=rp[x+1],lp[rp[x+1]]=y,flag?did[cnt][1]=rp[x+1]:0;
now=max(now,delta+1);
}
}
void recover(int u)
{
int x=did[u][2],y=did[u][0],z=did[u][1];
mark[x]=false;
if (y<x) rp[y]=x-1,lp[x-1]=y;
if (x<z) rp[x+1]=z,lp[z]=x+1;
}
void solve()
{
int tmp,tail;
for (int i=1;i<=m;i++)
{
if (i==1||(q[i-1].l-1)/s!=(q[i].l-1)/s)
{
memset(mark,0,sizeof mark);
tail=min(((q[i].l-1)/s+1)*s,n)+1;
now=0,lcur=tail,rcur=0;
}
while (rcur<q[i].r) change(++rcur,0);
cnt=0,tmp=now;
while (lcur>q[i].l) change(--lcur,1);
ans[q[i].id]=now,now=tmp,lcur=tail;
for (;cnt;) recover(cnt--);
}
}
int main()
{
freopen("seq.in","r",stdin),freopen("seq.out","w",stdout);
n=read(),m=read(),s=trunc(sqrt(n))+1;
for (int i=1;i<=n;i++) p[i]=read();
for (int i=1;i<=m;i++) q[i].l=read(),q[i].r=read(),q[i].id=i;
sort(q+1,q+1+m);
solve();
for (int i=1;i<=m;i++) printf("%d\n",ans[i]);
fclose(stdin),fclose(stdout);
return 0;
}