【题解】[bzoj 4358] permu【单增莫队】
题意
给定排列 \(p\),\(Q\) 次询问 \(p[l..r]\) 的最长值域连续段的长度。
\(1\leq n,m\leq 5\times 10^4\)
题解
记录每个值域连续段的端点往左/右能延伸的最大距离,往 \(p[l,r]\) 中加入一个数时可以 \(O(1)\) 维护。于是可以单增莫队。
(单增莫队做法:\(l,r\) 在同一块的直接暴力,其他询问按照 \(l\) 所在块(\([L,R]\))分组,每一组按 \(r\) 从小到大排序;处理时从 \([R,r]\) 转移到 \([R,r']\),再转移到 \([l',r']\) 得到答案,最后回滚到 \([R,r']\))
#include<bits/stdc++.h>
using namespace std;
int getint(){
int ans=0;
char c=getchar();
while(c<'0'||c>'9')c=getchar();
while(c>='0'&&c<='9'){
ans=ans*10+c-'0';
c=getchar();
}
return ans;
}
const int N=1e5+10;
int n,m,blk;
int p[N];
struct query{
int l,r,id;
};
bool cmp(const query &a,const query &b){ return a.r<b.r; }
vector<query>q[N];
int xl[N],xr[N];
vector<pair<int,int> >lm,rm;
int ans=0,oldans=0;
void addl(int x){
int v=p[x];
lm.emplace_back(v,xl[v]);
rm.emplace_back(v,xr[v]);
xl[v]=xl[v-1]?xl[v-1]:v;
xr[v]=xr[v+1]?xr[v+1]:v;
ans=max(ans,xr[v]-xl[v]+1);
if(xl[v-1]){
rm.emplace_back(xl[v-1],xr[xl[v-1]]);
xr[xl[v-1]]=xr[v];
}
if(xr[v+1]){
lm.emplace_back(xr[v+1],xl[xr[v+1]]);
xl[xr[v+1]]=xl[v];
}
}
void addr(int x){
int v=p[x];
xl[v]=xl[v-1]?xl[v-1]:v;
xr[v]=xr[v+1]?xr[v+1]:v;
ans=max(ans,xr[v]-xl[v]+1);
if(xl[v-1])
xr[xl[v-1]]=xr[v];
if(xr[v+1])
xl[xr[v+1]]=xl[v];
}
void rollback(){
ans=oldans;
for(int i=rm.size()-1;i>=0;--i)xr[rm[i].first]=rm[i].second;
for(int i=lm.size()-1;i>=0;--i)xl[lm[i].first]=lm[i].second;
rm.clear();
lm.clear();
}
int res[N];
int main(){
n=getint(),m=getint();
blk=max(1.0,n/sqrt(m*0.7));
for(int i=1;i<=n;i++)p[i]=getint();
for(int i=0;i<m;i++){
int l=getint(),r=getint();
if(l/blk==r/blk){
for(int i=r;i>=l;--i)addl(i);
res[i]=ans;
rollback();
continue;
}
query t;t.l=l;t.r=r;t.id=i;
q[l/blk].push_back(t);
}
for(int i=0;i<=n/blk;i++){
sort(q[i].begin(),q[i].end(),cmp);
memset(xl,0,sizeof(int)*(n+1));
memset(xr,0,sizeof(int)*(n+1));
ans=0;
int r=(i+1)*blk-1,l=r;
for(auto q: ::q[i]){
while(r<q.r)++r,addr(r);
oldans=ans;
for(int i=l;i>=q.l;--i)addl(i);
res[q.id]=ans;
rollback();
}
}
for(int i=0;i<m;i++)printf("%d\n",res[i]);
}