显然可以莫队维护权值建线段树最大连续子段长度,复杂度是 , 的数据下 大概为 ,再加一个 log 和大常数不太容易卡过去(有巨佬卡过去了)
考虑用并查集维护每个权值最左延申 和最右延申 ,答案就是 ,在莫队的过程中需要维护最大值,由于最大值不支持删除操作,需要用回滚莫队(即不删除莫队)
回滚莫队:
同一个块内的询问右边界是单调递增的,左边界是乱序但权值变换在
以内。
考虑每次询问前的 左边界先移到 这个块的右端点,每次将左边界移到询问左边界,询问结束将左边界撤回到块的右端点,中间的操作撤回。这样整个过程就只有插入操作没有删除操作。
需要分三类情况讨论:
1.询问的右边界和左边界在同一个块:
如果左边界从块的右端点移到询问左边界,可能会维护不在询问区间内的信息,但此时区间长度小于
因此直接暴力即可,询问完将维护的信息全部清空。
2.满足1的情况下,询问的左边界和上一个的询问的左边界不在同一个块
:这种清空由于 询问右边界 可能减小了,而右边界的操作无法撤回,因此只能暴力重新维护,询问结束后将左边界回退到块的右端点,由于不同的块只有
个,这种清空最多发生
次,复杂度为
3.满足1的情况下,询问的左边界和上一个询问的左边界在同一个块
:先将右边界移到询问右边界,接着将左边界移到询问左边界,得到答案后左边界移回到块右端点。
总体复杂度:
代码:
#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e6 + 10;
#define pii pair<int,int>
#define fir first
#define sec second
int block,n,m,a[maxn],curleft,curright,ans[maxn],L[maxn],R[maxn],res;
inline int read(){
int x=0;char ch=getchar();
while(ch<'0'||ch>'9') ch=getchar();
while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
return x;
}
struct ss{
int id,l,r;
ss(int x = 0,int y = 0,int z = 0) {
id = x; l = y; r = z;
}
bool operator < (const ss &rhs) const {
return l / block + (l % block > 0) == rhs.l / block + (rhs.l % block > 0) ? r < rhs.r : l < rhs.l;
}
//排序的分块方式和下面讨论的方式必须相同
}q[maxn];
int findL(int x) {
return x == L[x] ? x : L[x] = findL(L[x]);
}
int findR(int x) {
return x == R[x] ? x : R[x] = findR(R[x]);
}
void modify(int l,int r,int id) {
int pos = min((l / block + (l % block > 0)) * block,n);
if(r <= pos) {
res = 0;
for(int i = curleft; i <= curright; i++) //清空上一次,这种情况同样只会发生 根号 n 次
L[a[i]] = R[a[i]] = a[i];
for(int i = l; i <= r; i++) {
L[a[i]] = a[i] - 1;
R[a[i]] = a[i] + 1;
res = max(res,findR(a[i]) - findL(a[i]) - 1);
}
ans[id] = res;
for(int i = l; i <= r; i++)
L[a[i]] = R[a[i]] = a[i];
curleft = curright = pos;
L[a[pos]] = a[pos] - 1;
R[a[pos]] = a[pos] + 1;
res = 1;
} else {
if(l / block + (l % block > 0) != curleft / block + (curleft % block > 0)) { // 不同块只会发生 根号 n 次
int tpans = 0;
res = 0;
for(int i = curleft; i <= curright; i++) //清空上一次
L[a[i]] = R[a[i]] = a[i];
for(int i = pos; i <= r; i++) {
L[a[i]] = a[i] - 1;
R[a[i]] = a[i] + 1;
res = max(res,findR(a[i]) - findL(a[i]) - 1);
}
tpans = res;
for(int i = pos - 1; i >= l; i--) {
L[a[i]] = a[i] - 1;
R[a[i]] = a[i] + 1;
res = max(res,findR(a[i]) - findL(a[i]) - 1);
}
for(int i = l; i < pos; i++)
L[a[i]] = a[i], R[a[i]] = a[i];
ans[id] = res;
res = tpans;
curleft = pos,curright = r;
} else {
int tpans = 0;
while(curright < r) {
curright++;
L[a[curright]] = a[curright] - 1;
R[a[curright]] = a[curright] + 1;
res = max(res,findR(a[curright]) - findL(a[curright]) - 1);
}
tpans = res;
for(int i = pos - 1; i >= l; i--) {
L[a[i]] = a[i] - 1;
R[a[i]] = a[i] + 1;
res = max(res,findR(a[i]) - findL(a[i]) - 1);
}
for(int i = l; i < pos; i++)
L[a[i]] = a[i], R[a[i]] = a[i];
ans[id] = res;
res = tpans;
curleft = pos,curright = r;
}
}
}
int main() {
n = read(); m = read();
for(int i = 1; i <= n; i++)
a[i] = read();
for(int i = 1,l,r; i <= m; i++) {
l = read(); r = read();
q[i] = ss(i,l,r);
}
block = sqrt(n);
sort(q + 1,q + m + 1);
res = curleft = curright = 0;
for(int i = 0; i <= n + 1; i++)
L[i] = R[i] = i;
for(int i = 1; i <= m;i++) {
modify(q[i].l,q[i].r,q[i].id);
}
for(int i = 1; i <= m; i++)
printf("%d\n",ans[i]);
return 0;
}