回滚莫队

原理

对于删除操作的贡献很难统计的修改 可以选择使用回滚莫队

比如要求区间最值

显然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;
}

原文地址:https://www.cnblogs.com/2004-08-20/p/14262767.html