莫队算法——从入门到黑题

article/2025/8/8 20:35:51

众所周知,莫队是由莫涛大神提出的,一种玄学毒瘤暴力骗分区间操作算法,它以简短的框架、简单易记的板子和优秀的复杂度闻名于世。然而由于莫队算法应用的毒瘤,很多可做的莫队模板题都有着较高的难度评级,令很多初学者望而却步。然而,如果你真正理解了莫队的算法原理,那么它用起来还是很简单的。当然某些套左套右的毒瘤除外


0、前置芝士:

莫队算法还是比较独立的。不过你还是得了解了解以下的一些知识:

\(1\)、分块的基本思想(开根号等)

\(2\)、STL中sort的用法(手写cmp函数或重载运算符实现结构体的多关键字排序)

\(3\)、基(du)础(liu)的卡常技巧(包含#pragma GCC optimize系列)

\(4*\)、倍增/树剖 求LCA(树上莫队所需)

\(5*\)、数值离散化(用于应付很多题目)

至此全部完毕。撒花~~(雾

诶,别走啊qwq,我可不是在劝退qwq,如果你认为自己不懂这些东西也没关系,往下看吧qwq


1、莫队算法是个啥

来历:

前面已经介绍过了(逃

有兴趣的同学可以看一下莫涛大神的知乎

然而这个算法到底是用来搞什么操作的呢?我们先看个例题:

Luogu P1972 [SDOI2009]HH的项链

题目描述

HH 有一串由各种漂亮的贝壳组成的项链。HH 相信不同的贝壳会带来好运,所以每次散步完后,他都会随意取出一段贝壳,思考它们所表达的含义。HH 不断地收集新的贝壳,因此,他的项链变得越来越长。有一天,他突然提出了一个问题:某一段贝壳中,包含了多少种不同的贝壳?这个问题很难回答……因为项链实在是太长了。于是,他只好求助睿智的你,来解决这个问题。

输入输出格式

输入格式:

第一行:一个整数N,表示项链的长度。

第二行:N 个整数,表示依次表示项链中贝壳的编号(编号为0 到1000000 之间的整数)。

第三行:一个整数M,表示HH 询问的个数。

接下来M 行:每行两个整数,L 和R(1 ≤ L ≤ R ≤ N),表示询问的区间。

输出格式:

M 行,每行一个整数,依次表示询问对应的答案。

输入输出样例

输入样例#1:

6
1 2 3 4 3 5
3
1 2
3 5
2 6

输出样例#1:

2
2
4

说明

数据范围:

对于100%的数据,N <= 500000,M <= 500000。


题意简明易懂:给你一个长度不大于\(n≤5×10^5\)的序列,其中数值都小于等于\(10^6\),有\(m≤5×10^5\)次询问,每次询问区间\([l,r]\)中数值个数(也就是去重后数字的个数)。

不过这个例题卡了莫队,所以请左转数据弱化版:SP3267 DQUERY - D-query

题目到手,我们开始分析本题的算法。这题最简单做法无非暴力——用一个\(cnt\)数组记录每个数值出现的次数,再暴力枚举\(l\)\(r\)统计次数,最后再扫一遍cnt数组,统计\(cnt\)不为零的数值个数,输出答案即可。设最大数值为\(s\),那么这样做的复杂度为\(O(m(n+s))∽O(n^2)\),对于本题实在跑不起。

我们可以尝试优化一下:

优化1:每次枚举到一个数值\(num\),增加出现次数时判断一下\(cnt_{num}\)是否为0,如果为0,则这个数值之前没有出现过,现在出现了,数值数当然要+1。反之在从区间中删除\(num\)后也判断一下\(cnt_{num}\)是否为0,如果为0数值总数-1。这样我们优化掉了一个\(O(ms)\),但还是跑不起。

优化2:我们弄两个指针 \(l\)\(r\) ,每次询问不直接枚举,而是移动 \(l\)\(r\) 指针到询问的区间,直到\([l,r]\)与询问区间重合。在统计答案时,我们也只在两个指针处加减\(cnt\),然后我们就可以用优化1中的方法快速地统计答案啦\(qwq\)


优化2具体步骤如下:

假设这个序列是这样子的:(其中\(Q1\)\(Q2\)是询问区间)

1539583-20181214111957440-289670786.jpg

我们初始化\(l=1\)\(r=0\)(如果\(l=0\),那么我们还需要删除一个数值\(0\),使其出现次数变成-1,导致一些奇奇怪怪错误),如下图(由于画图软件中\(l\)\(1\)看不出区别,我只好在图中使用\(L\)\(R\)来表示qwq):

1539583-20181214112312924-142905583.jpg

我们发现 \(l\) 已经是第一个查询区间的左端点,无需移动。现在我们将 \(r\) 右移一位,发现新数值1:

1539583-20181214132130120-1554450961.jpg

\(r\) 继续右移,发现新数值2:

1539583-20181214132211435-482551090.jpg

继续右移,发现新数值4:

1539583-20181214132245702-439750869.jpg

\(r\) 再次右移时,发现此时的新位置中的数值2出现过,数值总数不增:

1539583-20181214132410275-1972965271.jpg

接下来是两个7,由于7没出现过,所以总数+1:

1539583-20181214132450055-1521044095.jpg

继续右移发现3:

1539583-20181214132530925-1512284326.jpg

继续右移,但接下来的两个数值都出现过,总数不增。

1539583-20181214132620562-1439006141.jpg

至此,\(Q1\)区间所有数值统计完成,结果为5。

现在我们又看一下\(Q2\)区间的情况:

首先我们发现, \(l\) 指针在\(Q2\)区间左端点的左边,我们需要将它右移,同时删除原位置的统计信息。

\(l\)右移一位到位置2,删除位置1处的数值1。但由于操作后的区间中仍然有数值1存在,所以总数不减。

1539583-20181214134652168-9341337.jpg

接下来的两位也是如此,直接删掉即可,总数不减。

1539583-20181214134950911-245687742.jpg

\(l\) 指针继续右移时,发现一个问题:原位置上的数值是2,但是删除这个2后,此时的区间\([l,r]\)中再也没有2了(回顾之前的内容,这种情况就是删除后\(cnt_2 = 0\)),那么总数就要-1,因为有一个数值已经不在该区间内出现了,而本题需要统计的就是区间内的数值个数。此步骤如下图:

1539583-20181214141945700-1377126698.jpg

再右移一位,发现无需减总数,而且\(l\)已经移到了\(Q2\)区间的左端点,无需继续移下去(如下图)。当然 \(r\) 还是要移动的,只不过没图了,我相信大家应该知道做法的\(qwq\)

1539583-20181214142409184-977743178.jpg

\(r\)的最后位置:

1539583-20181214142435118-121917103.jpg

至于删除操作,也是一样的做法,只不过要先删除当前位置的数值,才能移动指针。

有了以上的内容,这段代码就可以很容易写出啦qwq:

int aa[maxn], cnt[maxn], l = 1, r = 0, now = 0; //每个位置的数值、每个数值的计数器、左指针、右指针、当前统计结果(总数)
void add(int pos) {//添加一个数if(!cnt[aa[pos]]) ++now;//在区间中新出现,总数要+1++cnt[aa[pos]];
}
void del(int pos) {//删除一个数--cnt[aa[pos

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

相关文章

莫队(模板 + 带简单修改的莫队)

莫队 这个算法的思想比较简单&#xff0c;我们在做RMQ类问题时&#xff0c;有多次询问的那种&#xff0c;其实在这些询问中有很多都是问的同一段区间&#xff0c;即有的区间被询问的多次&#xff0c;所以我们对询问进行一个排序&#xff0c;假设上次询问我们得到了区间[l,r]的…

莫队 - 基础与扩展

普通莫队 莫队可以说是一个算法&#xff0c;但更多是一种思想。 我们先来看看普通莫队解决的问题&#xff1a; 有一个长度为 n n n 的数列 a a a。 q q q 个询问&#xff1a; a a a 在 [ l i , r i ] [{l_i},r_i] [li​,ri​] 中有多少个不同的数。 不强制在线。 1 ≤ n …

莫队详解

莫队实际很简&#xff08;du&#xff09;单&#xff08;liu&#xff09; 依照某位dalao的说法&#xff0c;就是两只小手&#xff08;two-pointers&#xff09;瞎跳 一.莫队&#xff08;静态莫队&#xff09; 我们以Luogu P3901 数列找不同为例讲一下静态莫队 这道题是个绿题&am…

关于莫队

待补&#xff1a;树上莫队&#xff0c;二次离线莫队 关于莫队 其实是个神奇的暴力算法.. 通常就是一种处理区间的东西 通过上一次的的左端点和右端点跳来跳去 从而得到这次区间的值 很暴力啊...完全不用思考就能懂的 那么 接下来首先是 普通莫队 排序和移动两点提及一下 …

莫队(普通莫队,带修莫队,树上莫队)

听说 莫队算法是一种“优雅的暴力”&#xff08;小声bb&#xff09;。 普通莫队 1/ 引入 problem&#xff1a;给你一个长度为n的数组&#xff0c;有m次查询&#xff0c;每次查询询问一个区间[L, R]内有多少个不同的数。 首先想想暴力怎么做。 开一个数组用来记数&#xff0c…

回滚莫队解析

&#xff08;南昌理工ACM集训队&#xff09; 泻药&#xff0c;不匿&#xff08;bushi&#xff09; 浅谈莫队回滚莫队样例小结 浅谈莫队 莫队是要摸清回滚莫队必须掌握的前置技能(๑ŐдŐ)b 前两天打多校有幸接触了莫队算法&#xff0c;花了几天时间搞懂了这些东西&#xff0…

【莫队/树上莫队/回滚莫队】原理详解及例题:小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…