文章目录
解释摆了大家都做数学了我还搁这里莫队()
写cpp里了
【模板】回滚莫队&不删除莫队
###题目链接
题目背景
这是一道模板题。
题目描述
给定一个序列,多次询问一段区间 [ l , r ] [l,r] [l,r],求区间中相同的数的最远间隔距离。
序列中两个元素的间隔距离指的是两个元素下标差的绝对值。
输入格式
第一行一个整数 n n n,表示序列长度。
第二行 n n n 个整数,描述这个序列。
第三行一个整数 m m m,表示询问个数。
之后 m m m 行,每行两个整数 l , r l,r l,r 表示询问区间。
输出格式
共 m m m 行,每行一个整数表示答案。如果区间内不存在两个数相同,则输出 0 0 0。
样例 #1
样例输入 #1
8
1 6 2 2 3 3 1 6
5
1 4
2 5
2 8
5 6
1 7
样例输出 #1
1
1
6
1
6
提示
记 a i a_i ai 表示序列元素。
对于 40 % 40\% 40% 的数据,满足 1 ≤ a i ≤ 400 1\leq a_i \leq 400 1≤ai≤400, 1 ≤ n , m ≤ 60000 1\leq n,m\leq 60000 1≤n,m≤60000。
对于 100 % 100\% 100% 的数据,满足 1 ≤ n , m ≤ 2 ⋅ 1 0 5 1\leq n,m\leq 2\cdot 10^5 1≤n,m≤2⋅105, 1 ≤ a i ≤ 2 ⋅ 1 0 9 1\leq a_i\leq 2\cdot 10^9 1≤ai≤2⋅109。
完整代码
Miku's Code#include<bits/stdc++.h>
using namespace std;
/*
回滚莫队(×)
能不删不删莫队(√)
我们的普通莫队,删除操作就要更改答案。
但是回滚莫队的思想就是让我们的删除操作更少。
那么如何实现?
我们的普通莫队l\r指针反复横跳
那么我们的回滚莫队就是不让它瞎跳:
我们找到几个询问作为一组,在这一组询问里找一个公共线段,使这个公共线段的l\r不断移动达到与查询区间左右端点重合的目的
而l\r的移动,我们让它有序,也就是说让它的这组询问l/r不回撤
如何处理?
我们设左端点在一个块内所有的询问构成一组询问。
莫队排序后一组询问的右端点有序、
可知左端点是无序的,但是他们在一个分块里。
我们使l停留在这组询问左端点块的最右方,r不断向右移动处理询问。
对于每个询问的左端点定义一个临时指针p和一个临时的数组to2从L开始向左处理询问,L不变,p查完回撤,清空to2
而下一组我们直接清空数组重新设定l/r即可
*/
const int maxn=2e5+50;
int n,m,sqn,temp[maxn]; //temp是离散化后的x序列
int bl[maxn],L[maxn],R[maxn]; //分块
int ans[maxn]; //离线答案
int st[maxn],to1[maxn],to2[maxn];
/*
在一定范围内:
st表示元素出现的第一个位置
to1表示元素出现最后一个位置
to2表示每组询问的公共起点(某个块的右端)前的一段中,元素出现的最后一个位置
*/
struct lsh{
int id,x;
};lsh s[maxn]; //序列
struct MO{
int l,r,id;
};MO q[maxn]; //查询
bool comp(MO m1,MO m2){
if(bl[m1.l]==bl[m2.l]){
return m1.r<m2.r;
}
return m1.l<m2.l;
}
bool scomp(lsh l1,lsh l2){ //离散化使用
return l1.x<l2.x;
}
void input(){
scanf("%d",&n);
sqn=sqrt(n);
for(int i=1;i<=n;++i){
scanf("%d",&s[i].x);
s[i].id=i;
bl[i]=(i-1)/sqn+1;
if(bl[i]!=bl[i-1]) L[bl[i]]=i,R[bl[i-1]]=i-1;
}
R[bl[n]]=n;
scanf("%d",&m);
for(int i=1;i<=m;++i){
scanf("%d%d",&q[i].l,&q[i].r);
q[i].id=i;
}
}
void pre(){ //离散化
sort(s+1,s+1+n,scomp);
int pre=-1,cnt=0;
for(int i=1;i<=n;++i){ //不知道结构体咋用lower_bound和unique所以摆了
if(s[i].x!=pre) ++cnt;
temp[s[i].id]=cnt;
pre=s[i].x;
}
}
void work(){
int bll=0,ss=0,l=0,r=0;
//bll表示上次询问左端点所在分块,ss是上次在L-R区间里查询到的答案
//l是公共的,r是上次询问的
for(int i=1;i<=m;++i){
if(bl[q[i].l]==bl[q[i].r]){ //在一个分块里暴力就好
ss=0;
for(int j=q[i].l;j<=q[i].r;++j){
st[temp[j]]=0;
}
for(int j=q[i].l;j<=q[i].r;++j){
if(!st[temp[j]]) st[temp[j]]=j;
ss=max(ss,j-st[temp[j]]);
}
for(int j=q[i].l;j<=q[i].r;++j){
st[temp[j]]=0;
}
ans[q[i].id]=ss;
continue;
}
if(bll!=bl[q[i].l]){
//左端点块发生变化我们把之前求解的东西全都不要了。
ss=0;
for(int j=l;j<=r;++j) st[temp[j]]=to1[temp[j]]=0;
l=R[bl[q[i].l]]; //l变成现在的查询左端点区块的右端点
r=l-1; //莫队初始化
bll=bl[q[i].l]; //更新分块
}
while(r<q[i].r){ //计算右端点后一小节
++r;
if(!st[temp[r]]) st[temp[r]]=r;
to1[temp[r]]=r; //元素最后出现的位置要一直更新啊
ss=max(ss,r-st[temp[r]]);
}
int p=l,ss2=0;
while(q[i].l<p){ //计算左端点前一小节
--p;
if(!to2[temp[p]]) to2[temp[p]]=p;
//我们从后向前开始移动,最后出现的位置只需要更新一次哦
ss2=max(ss2,max(to1[temp[p]],to2[temp[p]])-p);
//为什么要取max?因为我们的固定的左端点右边也可能有这个元素啊
}
while(p<l){ //回撤
to2[temp[p]]=0;
++p;
}
ans[q[i].id]=max(ss2,ss);
}
}
int main(){
input();
sort(q+1,q+1+m,comp);
pre();
work();
for(int i=1;i<=m;++i){
printf("%d\n",ans[i]);
}
return 0;
}
相似题目:
permu
题目链接
题目背景
[无]
题目描述
给出一个长度为 n n n的排列 P ( P 1 , P 2 , . . . P n ) P(P1,P2,...Pn) P(P1,P2,...Pn),以及 m m m个询问。每次询问某个区间 [ l , r ] [l,r] [l,r]中,最长的值域 连续段长度。
输入格式
第一行两个整数 n , m n,m n,m。
接下来一行 n n n个整数,描述 P P P。
接下来 m m m行,每行两个整数 l , r l,r l,r,描述一组询问。
输出格式
对于每组询问,输出一行一个整数,描述答案。
样例 #1
样例输入 #1
8 3
3 1 7 2 5 8 6 4
1 4
5 8
1 7
样例输出 #1
3
3
4
提示
对于询问 [ 1 , 4 ] [1,4] [1,4], P 2 , P 4 , P 1 P2,P4,P1 P2,P4,P1组成最长的值域连续段 [ 1 , 3 ] [1,3] [1,3];
对于询问 [ 5 , 8 ] [5,8] [5,8], P 8 , P 5 , P 7 P8,P5,P7 P8,P5,P7组成最长的值域连续段 [ 4 , 6 ] [4,6] [4,6];
对于询问 [ 1 , 7 ] [1,7] [1,7], P 5 , P 7 , P 3 , P 6 P5,P7,P3,P6 P5,P7,P3,P6组成最长的值域连续段 [ 5 , 8 ] [5,8] [5,8]。
1 < = n , m < = 50000 1<=n,m<=50000 1<=n,m<=50000
完整代码
来点仿照上一道题的WA0#include<bits/stdc++.h>
#include<bits/stdc++.h>
using namespace std;
const int maxn=2e5+50;
int n,m,sqn,a[maxn];
int bl[maxn],L[maxn],R[maxn];
int ans[maxn];
int lb[maxn],rb[maxn],slb[maxn],srb[maxn];
//lb[v]表示权值v左侧的连续段个数,rb[v]表示右侧的连续段个数
//只有增加值落在在段边界上的时候更新答案
struct MO{
int l,r,id;
};MO q[maxn]; //查询
bool comp(MO m1,MO m2){
if(bl[m1.l]==bl[m2.l]){
return m1.r<m2.r;
}
return m1.l<m2.l;
}
void input(){
scanf("%d%d",&n,&m);
sqn=sqrt(n);
for(int i=1;i<=n;++i){
scanf("%d",&a[i]);
bl[i]=(i-1)/sqn+1;
if(bl[i]!=bl[i-1]) L[bl[i]]=i,R[bl[i-1]]=i-1;
}
R[bl[n]]=n;
for(int i=1;i<=m;++i){
scanf("%d%d",&q[i].l,&q[i].r);
q[i].id=i;
}
}
void work(){
int bll=0,ss=0,l=0,r=0;
//bll表示上次询问左端点所在分块,ss是上次在L-R区间里查询到的答案
//l是公共的,r是上次询问的
for(int i=1;i<=m;++i){
if(bl[q[i].l]==bl[q[i].r]){ //在一个分块里暴力就好
ss=0;
for(int j=q[i].l;j<=q[i].r;++j){
lb[a[j]]=rb[a[j]]=0;
}
for(int j=q[i].l;j<=q[i].r;++j){
lb[a[j]]=lb[a[j]-1]+1;
rb[a[j]]=rb[a[j]+1]-1;
int tmp=lb[a[j]]+rb[a[j]]-1;
ss=max(ss,tmp);
lb[a[j]+rb[a[j]]-1]=tmp;
rb[a[j]-lb[a[j]]+1]=tmp;
}
for(int j=q[i].l;j<=q[i].r;++j){
lb[a[j]]=rb[a[j]]=0;
}
ans[q[i].id]=ss;
continue;
}
if(bll!=bl[q[i].l]){
//cout<<"###"<<i<<endl;
ss=0;
for(int j=l;j<=r;++j){
lb[a[j]]=rb[a[j]]=0;
}
l=R[bl[q[i].l]];
r=l-1;
bll=bl[q[i].l];
}
while(r<q[i].r){ //计算右端点后一小节
++r;
lb[a[r]]=lb[a[r]-1]+1;
rb[a[r]]=rb[a[r]+1]+1;
int tmp=lb[a[r]]+rb[a[r]]-1;
ss=max(tmp,ss);
lb[a[r]+rb[a[r]]-1]=tmp;
rb[a[r]-lb[a[r]]+1]=tmp;
}
int p=l,ss2=ss;
if(q[i].l<p){
for(int j=l;j<=r;++j){
slb[a[j]]=lb[a[j]];
srb[a[j]]=rb[a[j]];
}
}
while(q[i].l<p){ //计算左端点前一小节
--p;
slb[a[p]]=slb[a[p]-1]+1;
srb[a[p]]=srb[a[p]+1]+1;
int tmp=slb[a[p]]+srb[a[p]]-1;
ss2=max(ss2,tmp);
slb[a[p]+rb[a[p]]-1]=tmp;
srb[a[p]-lb[a[p]]-1]=tmp;
}
for(int j=p;j<=r;++j){ //回撤
slb[a[j]]=srb[a[j]]=0;
}
p=l;
ans[q[i].id]=max(ss2,ss);
}
}
int main(){
input();
sort(q+1,q+1+m,comp);
work();
for(int i=1;i<=m;++i){
printf("%d\n",ans[i]);
}
return 0;
}
//可以看到这一道题又T又WA,F了
//真不会改,就算WA能该对T怎么改啊aaaaa
Miku's Code
#include<bits/stdc++.h>
using namespace std;
const int maxn=5e4+50;
int n,m,sqn,a[maxn];
int bl[maxn],L[maxn],R[maxn];
int ans[maxn];
int lb[maxn],rb[maxn],slb[maxn],srb[maxn];
//lb[v]表示权值v左侧的连续段个数,rb[v]表示右侧的连续段个数
//只有增加值落在在段边界上的时候更新答案
struct MO{
int l,r,id;
};MO q[maxn]; //查询
struct SAVE{
int type; //记录修改了哪个数组,1=lb,2=rb
int pos,val;
};SAVE save[maxn];
bool comp(MO m1,MO m2){
if(bl[m1.l]==bl[m2.l]){
return m1.r<m2.r;
}
return m1.l<m2.l;
}
void input(){
scanf("%d%d",&n,&m);
sqn=sqrt(n);
for(int i=1;i<=n;++i){
scanf("%d",&a[i]);
bl[i]=(i-1)/sqn+1;
if(bl[i]!=bl[i-1]) L[bl[i]]=i,R[bl[i-1]]=i-1;
}
R[bl[n]]=n;
for(int i=1;i<=m;++i){
scanf("%d%d",&q[i].l,&q[i].r);
q[i].id=i;
}
}
void work(){
int bll=0,ss=0,r=0,ss2=0;
//bll表示上次询问左端点所在分块,ss是上次在L-R区间里查询到的答案
//l是公共的,r是上次询问的
for(int i=1;i<=m;++i){
if(bll!=bl[q[i].l]){
ss=0;
for(int j=1;j<=n;++j){
lb[a[j]]=rb[a[j]]=0;
}
//l=R[bl[q[i].l]];
r=bl[q[i].l]*sqn;
bll=bl[q[i].l];
}
while(r<q[i].r){ //计算右端点后一小节
++r;
lb[a[r]]=lb[a[r]-1]+1;
rb[a[r]]=rb[a[r]+1]+1;
int tmp=lb[a[r]]+rb[a[r]]-1;
ss=max(tmp,ss);
lb[a[r]+rb[a[r]]-1]=tmp;
rb[a[r]-lb[a[r]]+1]=tmp;
}
ss2=ss; //还是不对ss直接修改
int p=0;
for(int j=q[i].l;j<=min(q[i].r,R[bl[q[i].l]]);++j){
lb[a[j]]=lb[a[j]-1]+1;
rb[a[j]]=rb[a[j]+1]+1;
int tmp=lb[a[j]]+rb[a[j]]-1;
save[++p].type=1; save[p].pos=a[j]+rb[a[j]]-1; save[p].val=lb[a[j]+rb[a[j]]-1];
save[++p].type=2; save[p].pos=a[j]-lb[a[j]]+1; save[p].val=rb[a[j]-lb[a[j]]+1];
//记录修改之前的值
lb[a[j]+rb[a[j]]-1]=tmp;
rb[a[j]-lb[a[j]]+1]=tmp;
ss2=max(ss2,tmp);
}
for(int j=p;j>=1;--j){ //回撤
if(save[j].type==1) lb[save[j].pos]=save[j].val;
else rb[save[j].pos]=save[j].val;
}
for(int j=q[i].l;j<=min(q[i].r,R[bl[q[i].l]]);++j){ //回撤
lb[a[j]]=rb[a[j]]=0;
}
ans[q[i].id]=ss2;
}
}
int main(){
input();
sort(q+1,q+1+m,comp);
work();
for(int i=1;i<=m;++i){
printf("%d\n",ans[i]);
}
return 0;
}
@[toc] # 解释摆了大家都做数学了我还搁这里莫队() 写cpp里了
【模板】回滚莫队&不删除莫队
###题目链接
题目背景
这是一道模板题。
题目描述
给定一个序列,多次询问一段区间 [ l , r ] [l,r] [l,r],求区间中相同的数的最远间隔距离。
序列中两个元素的间隔距离指的是两个元素下标差的绝对值。
输入格式
第一行一个整数 n n n,表示序列长度。
第二行 n n n 个整数,描述这个序列。
第三行一个整数 m m m,表示询问个数。
之后 m m m 行,每行两个整数 l , r l,r l,r 表示询问区间。
输出格式
共 m m m 行,每行一个整数表示答案。如果区间内不存在两个数相同,则输出 0 0 0。
样例 #1
样例输入 #1
8
1 6 2 2 3 3 1 6
5
1 4
2 5
2 8
5 6
1 7
样例输出 #1
1
1
6
1
6
提示
记 a i a_i ai 表示序列元素。
对于 40 % 40\% 40% 的数据,满足 1 ≤ a i ≤ 400 1\leq a_i \leq 400 1≤ai≤400, 1 ≤ n , m ≤ 60000 1\leq n,m\leq 60000 1≤n,m≤60000。
对于 100 % 100\% 100% 的数据,满足 1 ≤ n , m ≤ 2 ⋅ 1 0 5 1\leq n,m\leq 2\cdot 10^5 1≤n,m≤2⋅105, 1 ≤ a i ≤ 2 ⋅ 1 0 9 1\leq a_i\leq 2\cdot 10^9 1≤ai≤2⋅109。
完整代码
Miku's Code#include<bits/stdc++.h>
using namespace std;
/*
回滚莫队(×)
能不删不删莫队(√)
我们的普通莫队,删除操作就要更改答案。
但是回滚莫队的思想就是让我们的删除操作更少。
那么如何实现?
我们的普通莫队l\r指针反复横跳
那么我们的回滚莫队就是不让它瞎跳:
我们找到几个询问作为一组,在这一组询问里找一个公共线段,使这个公共线段的l\r不断移动达到与查询区间左右端点重合的目的
而l\r的移动,我们让它有序,也就是说让它的这组询问l/r不回撤
如何处理?
我们设左端点在一个块内所有的询问构成一组询问。
莫队排序后一组询问的右端点有序、
可知左端点是无序的,但是他们在一个分块里。
我们使l停留在这组询问左端点块的最右方,r不断向右移动处理询问。
对于每个询问的左端点定义一个临时指针p和一个临时的数组to2从L开始向左处理询问,L不变,p查完回撤,清空to2
而下一组我们直接清空数组重新设定l/r即可
*/
const int maxn=2e5+50;
int n,m,sqn,temp[maxn]; //temp是离散化后的x序列
int bl[maxn],L[maxn],R[maxn]; //分块
int ans[maxn]; //离线答案
int st[maxn],to1[maxn],to2[maxn];
/*
在一定范围内:
st表示元素出现的第一个位置
to1表示元素出现最后一个位置
to2表示每组询问的公共起点(某个块的右端)前的一段中,元素出现的最后一个位置
*/
struct lsh{
int id,x;
};lsh s[maxn]; //序列
struct MO{
int l,r,id;
};MO q[maxn]; //查询
bool comp(MO m1,MO m2){
if(bl[m1.l]==bl[m2.l]){
return m1.r<m2.r;
}
return m1.l<m2.l;
}
bool scomp(lsh l1,lsh l2){ //离散化使用
return l1.x<l2.x;
}
void input(){
scanf("%d",&n);
sqn=sqrt(n);
for(int i=1;i<=n;++i){
scanf("%d",&s[i].x);
s[i].id=i;
bl[i]=(i-1)/sqn+1;
if(bl[i]!=bl[i-1]) L[bl[i]]=i,R[bl[i-1]]=i-1;
}
R[bl[n]]=n;
scanf("%d",&m);
for(int i=1;i<=m;++i){
scanf("%d%d",&q[i].l,&q[i].r);
q[i].id=i;
}
}
void pre(){ //离散化
sort(s+1,s+1+n,scomp);
int pre=-1,cnt=0;
for(int i=1;i<=n;++i){ //不知道结构体咋用lower_bound和unique所以摆了
if(s[i].x!=pre) ++cnt;
temp[s[i].id]=cnt;
pre=s[i].x;
}
}
void work(){
int bll=0,ss=0,l=0,r=0;
//bll表示上次询问左端点所在分块,ss是上次在L-R区间里查询到的答案
//l是公共的,r是上次询问的
for(int i=1;i<=m;++i){
if(bl[q[i].l]==bl[q[i].r]){ //在一个分块里暴力就好
ss=0;
for(int j=q[i].l;j<=q[i].r;++j){
st[temp[j]]=0;
}
for(int j=q[i].l;j<=q[i].r;++j){
if(!st[temp[j]]) st[temp[j]]=j;
ss=max(ss,j-st[temp[j]]);
}
for(int j=q[i].l;j<=q[i].r;++j){
st[temp[j]]=0;
}
ans[q[i].id]=ss;
continue;
}
if(bll!=bl[q[i].l]){
//左端点块发生变化我们把之前求解的东西全都不要了。
ss=0;
for(int j=l;j<=r;++j) st[temp[j]]=to1[temp[j]]=0;
l=R[bl[q[i].l]]; //l变成现在的查询左端点区块的右端点
r=l-1; //莫队初始化
bll=bl[q[i].l]; //更新分块
}
while(r<q[i].r){ //计算右端点后一小节
++r;
if(!st[temp[r]]) st[temp[r]]=r;
to1[temp[r]]=r; //元素最后出现的位置要一直更新啊
ss=max(ss,r-st[temp[r]]);
}
int p=l,ss2=0;
while(q[i].l<p){ //计算左端点前一小节
--p;
if(!to2[temp[p]]) to2[temp[p]]=p;
//我们从后向前开始移动,最后出现的位置只需要更新一次哦
ss2=max(ss2,max(to1[temp[p]],to2[temp[p]])-p);
//为什么要取max?因为我们的固定的左端点右边也可能有这个元素啊
}
while(p<l){ //回撤
to2[temp[p]]=0;
++p;
}
ans[q[i].id]=max(ss2,ss);
}
}
int main(){
input();
sort(q+1,q+1+m,comp);
pre();
work();
for(int i=1;i<=m;++i){
printf("%d\n",ans[i]);
}
return 0;
}
相似题目:
permu
题目链接
题目背景
[无]
题目描述
给出一个长度为 n n n的排列 P ( P 1 , P 2 , . . . P n ) P(P1,P2,...Pn) P(P1,P2,...Pn),以及 m m m个询问。每次询问某个区间 [ l , r ] [l,r] [l,r]中,最长的值域 连续段长度。
输入格式
第一行两个整数 n , m n,m n,m。
接下来一行 n n n个整数,描述 P P P。
接下来 m m m行,每行两个整数 l , r l,r l,r,描述一组询问。
输出格式
对于每组询问,输出一行一个整数,描述答案。
样例 #1
样例输入 #1
8 3
3 1 7 2 5 8 6 4
1 4
5 8
1 7
样例输出 #1
3
3
4
提示
对于询问 [ 1 , 4 ] [1,4] [1,4], P 2 , P 4 , P 1 P2,P4,P1 P2,P4,P1组成最长的值域连续段 [ 1 , 3 ] [1,3] [1,3];
对于询问 [ 5 , 8 ] [5,8] [5,8], P 8 , P 5 , P 7 P8,P5,P7 P8,P5,P7组成最长的值域连续段 [ 4 , 6 ] [4,6] [4,6];
对于询问 [ 1 , 7 ] [1,7] [1,7], P 5 , P 7 , P 3 , P 6 P5,P7,P3,P6 P5,P7,P3,P6组成最长的值域连续段 [ 5 , 8 ] [5,8] [5,8]。
1 < = n , m < = 50000 1<=n,m<=50000 1<=n,m<=50000
完整代码
来点仿照上一道题的WA0#include<bits/stdc++.h>
#include<bits/stdc++.h>
using namespace std;
const int maxn=2e5+50;
int n,m,sqn,a[maxn];
int bl[maxn],L[maxn],R[maxn];
int ans[maxn];
int lb[maxn],rb[maxn],slb[maxn],srb[maxn];
//lb[v]表示权值v左侧的连续段个数,rb[v]表示右侧的连续段个数
//只有增加值落在在段边界上的时候更新答案
struct MO{
int l,r,id;
};MO q[maxn]; //查询
bool comp(MO m1,MO m2){
if(bl[m1.l]==bl[m2.l]){
return m1.r<m2.r;
}
return m1.l<m2.l;
}
void input(){
scanf("%d%d",&n,&m);
sqn=sqrt(n);
for(int i=1;i<=n;++i){
scanf("%d",&a[i]);
bl[i]=(i-1)/sqn+1;
if(bl[i]!=bl[i-1]) L[bl[i]]=i,R[bl[i-1]]=i-1;
}
R[bl[n]]=n;
for(int i=1;i<=m;++i){
scanf("%d%d",&q[i].l,&q[i].r);
q[i].id=i;
}
}
void work(){
int bll=0,ss=0,l=0,r=0;
//bll表示上次询问左端点所在分块,ss是上次在L-R区间里查询到的答案
//l是公共的,r是上次询问的
for(int i=1;i<=m;++i){
if(bl[q[i].l]==bl[q[i].r]){ //在一个分块里暴力就好
ss=0;
for(int j=q[i].l;j<=q[i].r;++j){
lb[a[j]]=rb[a[j]]=0;
}
for(int j=q[i].l;j<=q[i].r;++j){
lb[a[j]]=lb[a[j]-1]+1;
rb[a[j]]=rb[a[j]+1]-1;
int tmp=lb[a[j]]+rb[a[j]]-1;
ss=max(ss,tmp);
lb[a[j]+rb[a[j]]-1]=tmp;
rb[a[j]-lb[a[j]]+1]=tmp;
}
for(int j=q[i].l;j<=q[i].r;++j){
lb[a[j]]=rb[a[j]]=0;
}
ans[q[i].id]=ss;
continue;
}
if(bll!=bl[q[i].l]){
//cout<<"###"<<i<<endl;
ss=0;
for(int j=l;j<=r;++j){
lb[a[j]]=rb[a[j]]=0;
}
l=R[bl[q[i].l]];
r=l-1;
bll=bl[q[i].l];
}
while(r<q[i].r){ //计算右端点后一小节
++r;
lb[a[r]]=lb[a[r]-1]+1;
rb[a[r]]=rb[a[r]+1]+1;
int tmp=lb[a[r]]+rb[a[r]]-1;
ss=max(tmp,ss);
lb[a[r]+rb[a[r]]-1]=tmp;
rb[a[r]-lb[a[r]]+1]=tmp;
}
int p=l,ss2=ss;
if(q[i].l<p){
for(int j=l;j<=r;++j){
slb[a[j]]=lb[a[j]];
srb[a[j]]=rb[a[j]];
}
}
while(q[i].l<p){ //计算左端点前一小节
--p;
slb[a[p]]=slb[a[p]-1]+1;
srb[a[p]]=srb[a[p]+1]+1;
int tmp=slb[a[p]]+srb[a[p]]-1;
ss2=max(ss2,tmp);
slb[a[p]+rb[a[p]]-1]=tmp;
srb[a[p]-lb[a[p]]-1]=tmp;
}
for(int j=p;j<=r;++j){ //回撤
slb[a[j]]=srb[a[j]]=0;
}
p=l;
ans[q[i].id]=max(ss2,ss);
}
}
int main(){
input();
sort(q+1,q+1+m,comp);
work();
for(int i=1;i<=m;++i){
printf("%d\n",ans[i]);
}
return 0;
}
//可以看到这一道题又T又WA,F了
//真不会改,就算WA能该对T怎么改啊aaaaa
Miku's Code
#include<bits/stdc++.h>
using namespace std;
const int maxn=5e4+50;
int n,m,sqn,a[maxn];
int bl[maxn],L[maxn],R[maxn];
int ans[maxn];
int lb[maxn],rb[maxn],slb[maxn],srb[maxn];
//lb[v]表示权值v左侧的连续段个数,rb[v]表示右侧的连续段个数
//只有增加值落在在段边界上的时候更新答案
struct MO{
int l,r,id;
};MO q[maxn]; //查询
struct SAVE{
int type; //记录修改了哪个数组,1=lb,2=rb
int pos,val;
};SAVE save[maxn];
bool comp(MO m1,MO m2){
if(bl[m1.l]==bl[m2.l]){
return m1.r<m2.r;
}
return m1.l<m2.l;
}
void input(){
scanf("%d%d",&n,&m);
sqn=sqrt(n);
for(int i=1;i<=n;++i){
scanf("%d",&a[i]);
bl[i]=(i-1)/sqn+1;
if(bl[i]!=bl[i-1]) L[bl[i]]=i,R[bl[i-1]]=i-1;
}
R[bl[n]]=n;
for(int i=1;i<=m;++i){
scanf("%d%d",&q[i].l,&q[i].r);
q[i].id=i;
}
}
void work(){
int bll=0,ss=0,r=0,ss2=0;
//bll表示上次询问左端点所在分块,ss是上次在L-R区间里查询到的答案
//l是公共的,r是上次询问的
for(int i=1;i<=m;++i){
if(bll!=bl[q[i].l]){
ss=0;
for(int j=1;j<=n;++j){
lb[a[j]]=rb[a[j]]=0;
}
//l=R[bl[q[i].l]];
r=bl[q[i].l]*sqn;
bll=bl[q[i].l];
}
while(r<q[i].r){ //计算右端点后一小节
++r;
lb[a[r]]=lb[a[r]-1]+1;
rb[a[r]]=rb[a[r]+1]+1;
int tmp=lb[a[r]]+rb[a[r]]-1;
ss=max(tmp,ss);
lb[a[r]+rb[a[r]]-1]=tmp;
rb[a[r]-lb[a[r]]+1]=tmp;
}
ss2=ss; //还是不对ss直接修改
int p=0;
for(int j=q[i].l;j<=min(q[i].r,R[bl[q[i].l]]);++j){
lb[a[j]]=lb[a[j]-1]+1;
rb[a[j]]=rb[a[j]+1]+1;
int tmp=lb[a[j]]+rb[a[j]]-1;
save[++p].type=1; save[p].pos=a[j]+rb[a[j]]-1; save[p].val=lb[a[j]+rb[a[j]]-1];
save[++p].type=2; save[p].pos=a[j]-lb[a[j]]+1; save[p].val=rb[a[j]-lb[a[j]]+1];
//记录修改之前的值
lb[a[j]+rb[a[j]]-1]=tmp;
rb[a[j]-lb[a[j]]+1]=tmp;
ss2=max(ss2,tmp);
}
for(int j=p;j>=1;--j){ //回撤
if(save[j].type==1) lb[save[j].pos]=save[j].val;
else rb[save[j].pos]=save[j].val;
}
for(int j=q[i].l;j<=min(q[i].r,R[bl[q[i].l]]);++j){ //回撤
lb[a[j]]=rb[a[j]]=0;
}
ans[q[i].id]=ss2;
}
}
int main(){
input();
sort(q+1,q+1+m,comp);
work();
for(int i=1;i<=m;++i){
printf("%d\n",ans[i]);
}
return 0;
}