回滚莫队解析

article/2025/8/8 21:54:27

(南昌理工ACM集训队)

泻药,不匿(bushi)

  • 浅谈莫队
  • 回滚莫队
    • 样例
    • 小结

浅谈莫队

莫队是要摸清回滚莫队必须掌握的前置技能(๑ŐдŐ)b
前两天打多校有幸接触了莫队算法,花了几天时间搞懂了这些东西(我菜 〒▽〒 )

莫队是一个非常强的算法,它主要用于处理区间查询的问题,当区间查询的数量巨大且次数非常多时,普通的暴力查询最差达到O(n^n)的时间复杂度,而利用莫队算法可以大大缩短查询的时间。
莫队其实是利用对查询的离线处理,将查询排序,然后在前一个查询的基础上做数据的修改,道理其实非常简单,可以用一个模拟来代替抽象的言语形容

莫队算法排序小动画


(自己做的很简陋)

将查询分块并进行排序,左边在同一块的对右边排序,左边不在同一块的左边小的排前面。并且将排序后的询问依次进行处理,当处理完一个询问到下一个询问时,在之前的基础上加上比之前多出来的数据,减去比之前少的数据。通常分块的块的大小为 √ n,所以每处理一个询问时间复杂度大多都为√ n,因此总复杂度为O(n√ n),比之前的O(n^n)快了不少。

详细内容我就不说了,对前置技能莫队不了解的可以来看看隔壁此间大佬写的莫队博客

此间大犇yyds

回滚莫队

回滚莫队顾名思义就是 会滚的莫队 回滚的莫队,那么我们为什么要让它滚起来呢?

普通莫队面对大部分的查询我们都需要对查询进行排序,然后依次对排序之后的查询进行处理。处理的时候增加比上一个查询多出来的元素,减去比上一个查询少的元素。但是有些增加元素容易减去元素难的情况或者增加难减去容易的情况,直接莫队打会很麻烦。这个时候就需要想办法尽量减少删除元素的次数,或是直接不删除元素只增加元素。那么这就是回滚莫队的用武之地了。用撤回操作代替删除,然后对询问的排序方式进行修改,使处理查询的时候尽量减少撤回的次数
处理起来很麻烦,这也是一个大量空间换时间的算法

样例

P5906 【模板】回滚莫队&不删除莫队
一道题面非常简单的题

还有的人说AT1219 歴史の研究更像模板题,感兴趣的可以看看

题目描述

给定一个序列,多次询问一段区间 [l,r],求区间中相同的数的最远间隔距离。

序列中两个元素的间隔距离指的是两个元素下标差的绝对值。

对于 100% 的数据,满足 1≤n,m≤2⋅10 ^ 5 ,1≤ai​≤2⋅10 ^ 9。

对这道题而言,增加元素的时候只需更新相应的最右元素的下标,同时更新相应的最左元素的下标,然后用最右边的减去最左边的就可以得出当前元素的最远间隔距离。但是如果减去元素,那就要记录上一个元素,然后在减去元素的同时更改上一个元素,记录上上个元素…非常麻烦。这时利用回滚莫队,减少删除操作,使操作只有增加操作,即可快速且简单地求出询问的答案

个人认为,回滚莫队的精华就在于查询部分,只要搞定这部分,类似的题目做起来易如反掌(

对询问进行分块查询的时候,我们依次对排序之后的查询进行处理,而对于回滚莫队,我们需要排序之后尽量减少删除的次数,这时候我们可以想到,在不同的询问中间取一个点,然后查询方式以这个点为中心向两边延伸,就可以达到只增加不删除的目的。当以这个中心点向两边扩散的询问全部搜完时,我们删除所有临时创建的数据,重新定位下一个中心点,搜下一批的询问。这时我们就避免了删除操作,我们的操作等于撤回之前的所有操作(回到之前没搜过的样子),去处理剩下的所有询问。时间复杂度与普通莫队几乎没有差别,但我们这样做就避免了删除操作。

代码部分(剩余的详细注释都标注在代码里了)
本人小白如有不对欢迎指正ლ(╹◡╹ლ)

#include<iostream>
#include<algorithm>
#include<cmath>
#include<cstdio>
#include<cstring>
using namespace std;
const int N = 200010;
struct query {int l, r, id;//询问
}qy[N];
struct ls{int id, x;
}shu[N];
int ans[N], rd[N], R[N], block, Ans1 = 0, Ans2 = 0;
int tagl[N], tagr[N], b[N], ed2[N];
//tagl为元素最左出现的位置,tagr为元素最右出现的位置
bool cmp(query x, query y)//对询问进行排序,左边块相同的右边小的排前面,左边块不同的左边小的排前面
{return (b[x.l] == b[y.l]) ? x.r < y.r : b[x.l] < b[y.l];
}
bool cmp2(ls x,ls y)
{return x.x < y.x;//对输入的数据进行排序,用来离散化处理
}
void insertl(int x) {//插入左端的点if (!ed2[rd[x]])ed2[rd[x]] = x;//ed2为每次询问中的公共起点之前元素最后出现的位置Ans2 = max(Ans2, max(tagr[rd[x]], ed2[rd[x]]) - x);
}
void insertr(int x) {//插入右端的点if (!tagl[rd[x]])tagl[rd[x]] = x;tagr[rd[x]] = x;Ans1 = max(Ans1, x - tagl[rd[x]]);
}
void lsh(int n)//离散化
{sort(shu + 1, shu + n + 1, cmp2);//排序然后离散化int pre = -1, cnt = 0;for (int i = 1; i <= n; i++){if (shu[i].x != pre)cnt++;rd[shu[i].id] = cnt;//替换当前点的数据pre = shu[i].x;}
}
int read() {//快读防卡常int x = 0, f = 1;char c = getchar();while (c < '0' || c>'9') { if (c == '-') f = -1; c = getchar(); }while (c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar();return x * f;
}
void modui(int m)//开始处理询问 
{int bl = 0, l = 0, r = 0;Ans1 = 0;for (int i = 1; i <= m; i++){if (b[qy[i].l] == b[qy[i].r])//如果l和r的块相同那么直接暴力求答案就可以了{for (int j = qy[i].l; j <= qy[i].r; j++)tagl[rd[j]] = 0;//暴力循环之前将之前临时存的数据清零for (int j = qy[i].l; j <= qy[i].r; j++)//非常的暴力{if (!tagl[rd[j]])tagl[rd[j]] = j;ans[qy[i].id] = max(ans[qy[i].id], j - tagl[rd[j]]);}for (int j = qy[i].l; j <= qy[i].r; j++)tagl[rd[j]] = 0;continue;}int now = b[qy[i].l];//now为当前l的块if (bl != now)//如果不在一个块中则清空之前临时储存的数据,重置l和r{Ans1 = 0;for (int j = l; j <= r; j++)tagl[rd[j]] = tagr[rd[j]] = 0;l = R[now];//l定义为当前所属的块的最右边r = l - 1; bl = now;}while (r < qy[i].r)insertr(++r);//紧张刺激的莫队环节int p = l; Ans2 = 0;while (p > qy[i].l)insertl(--p);while (p < l){ed2[rd[p]] = 0;p++;}//循环结束之后重置ed2ans[qy[i].id] = max(Ans1, Ans2);//最后对比一下两端的答案即为最终答案}
}
int main()
{int n, m;scanf("%d", &n);block = pow(n, 0.5);//对块的大小进行定义,一般是取√ n,跟普通莫队无异for (int i = 1; i <= n; i++) {//输入数据shu[i].x = read();shu[i].id = i;b[i] = (i - 1) / block + 1;//存储块信息(减少计算量,不然每次都要计算一个l/block浪费时间)}for (int i = 1; i <= b[n]; i++)//处理出每个块的右端,存在R中 R[i] = (i == b[n]) ? n : block * i;lsh(n);//因为数据到10^9,而总共只有2*10^5,因此我们可以对数据进行离散化处理(这题有点卡常,能减少时间就尽量减)scanf("%d", &m);for (int i = 1; i <= m; i++) {qy[i].l = read(), qy[i].r = read(); qy[i].id = i;//输入询问数据,id记录询问顺序}sort(qy + 1, qy + 1 + m, cmp);//对询问进行离线处理modui(m);//莫队部分for (int i = 1; i <= m; i++)printf("%d\n", ans[i]);//按原本顺序输出答案}

小结

对于大量的区间查询,我们可以使用莫队算法来离线处理这些询问,如果遇到在线处理的询问,那么普通的莫队便难以应对了。当遇到删除难增加容易或者增加难删除容易的离线查询时,我们便可以利用回滚莫队的思路去解决这种问题。

谢谢你看到最后ヾ( ̄▽ ̄)ByeBye

学术垃圾


http://chatgpt.dhexx.cn/article/ZIFZ7gLl.shtml

相关文章

【莫队/树上莫队/回滚莫队】原理详解及例题:小B的询问(普通莫队),Count on a tree II(树上莫队),kangaroos(回滚莫队)

文章目录 问题引入介绍莫队算法及其实现过程时间复杂度莫队算法适用范围莫队奇偶优化普通莫队&#xff1a;小B的询问树上莫队&#xff1a;SP10707 COT2 - Count on a tree II回滚莫队&#xff1a;[PA2011]Kangaroos upd&#xff1a;2021-08-11&#xff1a;重新对博客进行了外观…

超全的莫队算法一遍过

前置知识&#xff1a;双指针、分块 简单概括而言&#xff0c;莫队是一个离线算法&#xff0c;核心思想是将所有的询问按照分块进行排序后&#xff0c;对于每个询问可以通过双指针增删数据来达到总体的复杂度。 莫队通常用来解决求一段区间内不同数的个数相关的问题。 目录 1、…

莫队(普通操作)C++

由于&R闲着无聊&#xff0c;今天上午学习的莫队也并不是完全掌握&#xff0c;老师才发的PPT也没有保存&#xff0c;于是决定乱写一篇靠着自己的理解写一篇关于莫队用法的博客......如果有错的麻烦之处&#xff0c;纯属&R自己理解有问题哈。 一.莫队的含义 莫队是在一…

【算法笔记】莫队算法(基础莫队,带修莫队,回滚莫队,树上莫队,二次离线莫队)

整理的算法模板合集&#xff1a; ACM模板 目录 1. 基础莫队2. 带修莫队3. 回滚莫队4. 树上莫队5. 二次离线莫队6. 在线莫队 来这里学习莫队以及神奇的证明&#xff1a;莫队算法 --算法竞赛专题解析&#xff08;26&#xff09; 我们首先考虑双指针的暴力法&#xff0c;发现很容…

linux(vscode)配置coderunner for python3

1.Python3 安装路径查看 which python3 2.vscode扩展中安装coderunner 3.配置coderunner 管理->设置->扩展->coderunner,找到下图所示内容&#xff0c;选择在settings.json中编辑。 在文件中输入code-runner.executorMap&#xff0c;会自动补全代码&#xff0c;如…

问题解决:VSCode Code Runner输出中文乱码

用vscode code runner插件经常会出现输出中文乱码的问题 那么如何解决这个问题呢&#xff1f; 解决方法&#xff1a; 通过设置&#xff0c;可以把代码放到 VS Code 内置的 Terminal 来运行&#xff0c;那么问题就能迎刃而解了。 选择 文件 -> 首选项 -> 设置&#xff0c…

VSCode——使用CodeRunner开发python输出中文出现乱码的解决方法

目录 方法一&#xff1a;每次都要加 方法二&#xff1a;一劳永逸 方法一 在程序中加入如下代码强行控制输出格式为utf-8即可 # -*- coding: utf-8 -*- import sys import io sys.stdoutio.TextIOWrapper(sys.stdout.buffer,encodingutf8)#改变默认输出的标准编码 编码改变…

修改CodeRunner快捷键

默认运行是 alt ctrl N, 改为alt ctrl r. run改更合理。 停止改为alt ctrl s stop也更合理&#x1f43b;

vscode用CodeRunner运行C++文件,提示系统找不到路径

问题描述 如图&#xff0c;直接点运行出现如下提示&#xff1a; 但是分开执行还是正常的&#xff1a; 原因 cmd执行多条语句要使用" && “作为连接符&#xff0c;对应的powerShell要用” ; "。 解决 在用户设置中修改code-runner.executorMap中对应的值…

vscode_Code Runner(codeRunner)配置(自定义输入映射内容)/指定.c/.cpp编译选型/编译运行含有中文名的文件示例(最佳实践)

文章目录 声明:文件(目录)中包含中文的问题关于配置调试功能(推荐) 找到配置入口点击在setting.json中编辑可以指定各种语言的命令行映射,这里以c语言指定以c99编译修改为例.可见到,届时编译.c文件的时候就会是以C99的标准指定编译. 运行效果 声明:文件(目录)中包含中文的问题 …

用VSCode 编写C++ 引入自定义头文件容易犯的错 和CodeRunner配置

问题描述 undefined reference to log(char const*) collect2.exe: error: ld returned 1 exit status 原因分析&#xff1a; 一般是由于c编译时&#xff0c;.cpp源文件未找到引起的 解决方案&#xff1a; 1.正确操作 一.例如你一个文件下有一个.h的文件和一个.cpp的文件 在v…

VSCode用Code Runner编译运行c/c++

首先vscode需要在文件夹下打开&#xff0c;以下所有的文件都需要放到你编写程序文件夹下的.vscode文件夹里 平时可以右击文件夹&#xff0c;选择open with code&#xff0c;结构应该是这样的 配置CodeRunner 下载 左下角齿轮找到设置 在扩展中找到Run Code configuration,点击…

【Mac】VScode配置Python开发环境详细教程(报错解决Import Error No module named ) CodeRunner插件

文章目录 在VScode中安装python插件解决报错SyntaxError: Non-ASCII character \xef in file解决报错"No module named xxx "VScode上使用Jupyter Notebook的方法20.02.26 最新方法&#xff1a;Code Runner插件一键安装&#xff08;python、java、C&#xff09;终端目…

CodeRunner插件:自定义编译参数

在设置里找到Coderunner选项&#xff0c;然后找到executorMap 点击 在 settings.json 中编辑 &#xff0c;你将看到对不同语言的编译指令。 以下为本人设置&#xff0c;为Mac系统&#xff0c;指令有所不同&#xff0c;不过操作流程是一样的。 "code-runner.executorMap…

java扩展包_CodeRunner 的 Java 扩展 Jar 包支持

CodeRunner 介绍 CodeRunner 是 Mac 上一款功能强大但使用简单代码工具&#xff0c;官方介绍支持几乎所有语言(20种语言)&#xff0c;同时支持语法高亮、代码提示和多种界面主题&#xff0c;在学习新的语言或编写简单测试代码时非常实用。 我常常用它来管理一些代码片段和测试不…

vscode 中 coderunner无法运行dart程序

如果你的vscode使用了CodeRunner&#xff0c;运行dart文件出错找不到dart的情况下: /bin/sh: dart: command not found可以直接修改coderunner扩展配置信息, 将其路径改为你希望的位置

vscode使用codeRunner将c/c++程序运行到外部控制台窗口

需要先关闭codeRunner的Run in Terminal 只需要在codeRunner的setting中&#xff0c;在从c/c的设置命令中添加 start cmd /c //或者/k /** 如果添加的是/c&#xff0c;则需要在程序的末尾添加system("pause"),不然会一闪而过&#xff0c; /k则不需要&#xff0c;不…

CodeRunner破解

CodeRunner2是Mac系统下的一款编程软件&#xff0c;支持23种语言和.txt文档制作&#xff0c;比Xcode都强大&#xff0c;Xcode只支持4种语言&#xff0c;原来的破解补丁有联网验证的问题&#xff0c;现在我做了程序防止联网验证。 破解方法&#xff1a;先下载CodeRunner2&#x…

windows系统VS code coderunner 运行shell脚本

无需设置环境变量&#xff0c;即可运行。 shell脚本第一行不要指定shell&#xff0c;什么“#!/bin/bash"不要写&#xff0c;这个会覆盖掉coderunner的设置。 在vs code设置中搜索“Code-runner: Executor Map” 点击“在setting.json中编辑” 修改shellscript后面引号里…

macOS使用CodeRunner快速配置fortran环境

个人网站:xzajyjs.cn 由于一些项目的缘故&#xff0c;需要有fortran的需求&#xff0c;但由于是M1 mac的缘故&#xff0c;不能像windows那样直接使用vsivf这种经典配置。搜了一下网上主流的跨平台方案&#xff0c;主要是gfortran&#xff0c;最近用Coderunner&#xff08;主要…