回滚莫队

简介:远看是莫队(r),近看是暴力(l,以及左右端点在同一块)。

还记得普通莫队里面怎么说的吗?

注意两个操作有时候会西掉一个,有时候还要在数据结构上操作,但这不在这篇文章的范围内。

所以,这篇文章就会讲述如何应对“两个操作西掉一个”的情况。

删除西掉了(更加常见)

和正常莫队的排序方法一样。

注意!由于回滚莫队的特殊性(不支持增加或删除),所以你不能在回滚莫队上使用奇偶排序。

对于每一个询问:

如果 l 换块了,将 l 初始化为这一个块的右端点 +1r 初始化为这一个块的右端点。

然后如下回答:

  • 如果这个询问只涉及到一个块,直接暴力。

  • 否则,将 r 滑动至这个询问的右端点,l 滑动至这个询问的左端点,记录答案后撤销 l 的更改,使得莫队回滚到正确的位置以应对下一个询问。

比如说区间最小值(我知道这可以用线段树,但是莫队可以解决一些没有结合律的问题),你无法在删除后得知新的最小值。这就是经典的删除西掉的情况。

增加西掉了

跟删除西掉的情况差不多:

对于每一个询问:

如果 l 换块了,将 l 初始化为这一个块的左端点,r 初始化为 N

然后如下回答:

  • 如果这个询问只涉及到一个块,直接暴力。

  • 否则,将 r 滑动至这个询问的右端点,l 滑动至这个询问的左端点,记录答案后撤销 l 的更改,使得莫队回滚到正确的位置以应对下一个询问。

比如说区间 mex(其实是另一种形式的最小值),你在增加一个元素后无法确定新的 mex。这种情况只能删除。

区间众数

这里介绍两种一般方法。

回滚莫队

这就很好想了,我们将所有数的出现次数取最大值,可知这是一个最大值问题,回滚莫队秒了。

优点是思维很好搞,缺点是不能在线,在强制在线的题目里用不了。

#include <bits/stdc++.h>
using namespace std;
const int kB = 450;
struct node {
int l, r, id;
}qry[400040];
int n, q, arr[100010], cnt[200020], ans[400040], bel[400040];
int main() {
ios::sync_with_stdio(0);
cin.tie(0), cout.tie(0);
cin >> n >> q;
for(int i = 1; i <= n; i++) {
cin >> arr[i];
bel[i] = i / kB + 1;
}
for(int i = 1; i <= q; i++) {
cin >> qry[i].l >> qry[i].r;
qry[i].id = i;
}
sort(qry + 1, qry + q + 1, [](node a, node b) {return bel[a.l] == bel[b.l]? a.r < b.r : bel[a.l] < bel[b.l];});
int l = 1, r = 0, s = 0;
for(int i = 1; i <= q; i++) {
if(bel[qry[i].l] != bel[qry[i - 1].l]) {
for(int i = l; i <= r; i++) {
cnt[arr[i] + 100010]--;
}
l = bel[qry[i].l] * kB, r = l - 1, s = 0;
}
if(bel[qry[i].l] == bel[qry[i].r]) {
int tmp = 0;
for(int j = qry[i].l; j <= qry[i].r; j++) {
cnt[arr[j] + 100010]++;
tmp = max(tmp, cnt[arr[j] + 100010]);
}
for(int j = qry[i].l; j <= qry[i].r; j++) {
cnt[arr[j] + 100010]--;
}
ans[qry[i].id] = tmp;
}else {
int tmp = 0;
for(; r < qry[i].r; ) {
r++;
cnt[arr[r] + 100010]++;
s = max(s, cnt[arr[r] + 100010]);
}
for(int j = qry[i].l; j < l; j++) {
cnt[arr[j] + 100010]++;
tmp = max(tmp, cnt[arr[j] + 100010]);
}
for(int j = qry[i].l; j < l; j++) {
cnt[arr[j] + 100010]--;
}
ans[qry[i].id] = max(s, tmp);
}
}
for(int i = 1; i <= q; i++) {
cout << ans[i] << '\n';
}
return 0;
}

分块

分块。记录块内区间的众数(n2=n,不会爆炸),块内直接记录答案,块外暴力更新答案。

优点是在线,缺点是不如莫队好搞。

posted @   hhc0001  阅读(14)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具
点击右上角即可分享
微信分享提示