原理
对于删除操作的贡献很难统计的修改 可以选择使用回滚莫队
比如要求区间最值
显然Max运算无法撤销 考虑如何快速处理删除贡献的操作
处理不了 所以换种思路 不再删除贡献
首先在莫队的sort时候 按照第一维bel,第二维r排序
此时对于每个左端点在同一个块内的询问, 此时r单调递增
所以对于r是没有删除操作的
考虑在块内无序排列的l 其实每次从块的右端点往左开始跑就可以了
每次暴力统计从块的右端点到l的贡献
因为块长不超过(√N) 所以每次暴力处理复杂度也是根号的
所以总复杂度(N√N)
当左右端点都在同一个块内时直接暴力即可
例题:
A:历史研究
求区间最值
维护块右端点右边每个事件出现的次数 (cnt[x]) 最大值Mx
维护块内每个事件出现次数 (tmp[x]) 最大值Max
对于询问 先把右端点转移到合适的位置 维护cnt与Mx
然后从块的右端点开始往左跑 每次维护tmp 与Max
当前事件的贡献就是((cnt[x] + tmp[x]) * x)
每次处理完将tmp清空就好了
B:permu
求区间最大连续段长度
删除操作不好处理 一样考虑回滚
在块的右端点右边维护 ld[x],l[x],rd[x],r[x] 分别表示x这个数向左最多多少长度是连续的, 最左边的数是多少,右边同理
以及此时的答案
在块内维护 tmpld[x],tmpl[x],tmprd[x],tmpr[x] 定义与块右边的相同
然后转移就可以了 每次加入一个新数 用左右两边的数更新它,再用它更新左右两边的值
tmp处理的时候有个细节是 如果此时tmpl[x-1]没有值, 需要用l[x-1]更新, r同理
每次更新维护答案 得解
复杂度 (N√N) 但是没有跑过(N√Nlog_N) 维护东西太多导致常数太大了
(C++ NOI (N√NlogN)跑不过去,这个能跑
(细节挺多但是一遍过了
显示代码
include<bits/stdc++.h>
using namespace std;
define rint register int
const int maxn = 1e5 + 10;
int tld[maxn],trd[maxn],tl[maxn],tr[maxn];
int l[maxn],r[maxn],ld[maxn],rd[maxn];
int stl[maxn],str[maxn];
int ans[maxn];
int a[maxn];
int Mx,sqr,cur,top1,top2;
struct node{
int l,r,bel,id;
friend bool operator <(const node &A,const node &B) {return A.bel == B.bel ? A.r < B.r : A.bel < B.bel;}
}q[maxn];
int main(){
rint n,m; scanf("%d%d",&n,&m); sqr = sqrt(n);
for(rint i = 1;i <= n;++i) scanf("%d",&a[i]);
for(rint i = 1;i <= m;++i) scanf("%d%d",&q[i].l,&q[i].r),q[i].bel = (q[i].l - 1) / sqr + 1, q[i].id = i;
sort(q+1,q+m+1);
rint tail = 0;
for(rint i = 1;i <= m;++i) {
rint L = q[i].l,R = q[i].r;
if(q[i].bel != q[i-1].bel) {
memset(l,0,sizeof(l)); memset(ld,0,sizeof(ld));
memset(r,0,sizeof(r)); memset(rd,0,sizeof(rd));
tail = q[i].bel * sqr; Mx = 0;
}
if(R <= q[i].bel * sqr) {
cur = 0;
for(rint j = L;j <= R;++j) {
tld[a[j]] = tld[a[j]-1] + 1;
trd[a[j]] = trd[a[j]+1] + 1;
tl[a[j]] = tld[a[j]-1] ? tl[a[j]-1] : a[j];
tr[a[j]] = trd[a[j]+1] ? tr[a[j]+1] : a[j];
if(tld[a[j]-1]) trd[tl[a[j]-1]] += trd[a[j]], tr[tl[a[j]-1]] = tr[a[j]];
if(trd[a[j]+1]) tld[tr[a[j]+1]] += tld[a[j]], tl[tr[a[j]+1]] = tl[a[j]];
cur = max(cur,tld[tr[a[j]]]);
}
for(rint j = L;j <= R;++j) tld[a[j]] = trd[a[j]] = tl[a[j]] = tr[a[j]] = 0;
ans[q[i].id] = cur;
continue;
}
while(tail < R) {
++tail;
ld[a[tail]] = ld[a[tail]-1] + 1;
rd[a[tail]] = rd[a[tail]+1] + 1;
l[a[tail]] = ld[a[tail]-1] ? l[a[tail]-1] : a[tail];
r[a[tail]] = rd[a[tail]+1] ? r[a[tail]+1] : a[tail];
if(ld[a[tail]-1]) rd[l[a[tail]-1]] += rd[a[tail]], r[l[a[tail]-1]] = r[a[tail]];
if(rd[a[tail]+1]) ld[r[a[tail]+1]] += ld[a[tail]], l[r[a[tail]+1]] = l[a[tail]];
Mx = max(Mx,ld[r[a[tail]]]);
}
cur = Mx;
for(rint j = L;j <= q[i].bel * sqr;++j) {
tld[a[j]] = (tld[a[j]-1] ? tld[a[j]-1] : ld[a[j]-1]) + 1;
trd[a[j]] = (trd[a[j]+1] ? trd[a[j]+1] : rd[a[j]+1]) + 1;
tl[a[j]] = (tld[a[j]-1] ? tld[a[j]-1] : ld[a[j]-1]) ? (tl[a[j]-1] ? tl[a[j]-1] : l[a[j]-1]) : a[j];
tr[a[j]] = (trd[a[j]+1] ? trd[a[j]+1] : rd[a[j]+1]) ? (tr[a[j]+1] ? tr[a[j]+1] : r[a[j]+1]) : a[j];
if(tld[a[j]-1] + ld[a[j]-1]) {
stl[++top1] = tl[a[j]-1] ? tl[a[j]-1] : l[a[j]-1];
rint x = stl[top1];
trd[x] = (trd[x] ? trd[x] : rd[x]) + trd[a[j]], tr[x] = tr[a[j]];
}
if(trd[a[j]+1] + rd[a[j]+1]) {
str[++top2] = tr[a[j]+1] ? tr[a[j]+1] : r[a[j]+1];
rint x = str[top2];
tld[x] = (tld[x] ? tld[x] : ld[x]) + tld[a[j]], tl[x] = tl[a[j]];
}
cur = max(cur,tld[tr[a[j]]]);
}
for(rint j = L;j <= q[i].bel*sqr;++j) tld[a[j]] = trd[a[j]] = tl[a[j]] = tr[a[j]] = 0;
while(top1) {
rint x = stl[top1];
trd[x] = tr[x] = 0;
top1--;
}
while(top2) {
rint x = str[top2];
tld[x] = tl[x] = 0;
top2--;
}
ans[q[i].id] = cur;
}
for(rint i = 1;i <= m;++i) printf("%d
",ans[i]);
return 0;
}