sqrtqwq
2024-11-02 08:40:44
本文为莫队学习笔记,如果有错误,请提出,谢谢捏。
窝的题单可以配套使用
同于与cnblog
假如说通过区间
相当于在平面直角坐标系上有
先把所有的问题离线下来,然后以
然后求答案即可。放一个移动求答案的代码。
我们知道如果按照
但是我们可以直接到尽头时向上走,然后倒退回去,这样这两行只需要两次就可以遍历完。但是不这样就需要四次。
所以我们可以在奇数行的时候按照普通的,否则我们倒着排序。
莫队模板题。
对于
然后我们用莫队维护
code
知识点:分块+莫队。
题目大意
在区间
Solution
如果我们直接利用莫队,那我们需要在用一个 BIT 来维护某个值域区间中的数,这样带
莫队算法的复杂度主要在修改上,其修改复杂度为
所以复杂度即为
code
首先我们对于操作
对于区间问题,我们考虑使用莫队进行维护。
我们记录当前
首先我们先考虑指针
直接交换
直接给
然后我们考虑指针
我们同样先交换
由于这个操作会影响到后面的查询操作,所以这个操作会改变答案。所以在交换后我们需要加上
和上面一样,
剩下两种类似,只需改改符号即可。时间复杂度
最后要注意本题对
code
众所周知,莫队是不以直接修改的,但是题目有时候会要求我们修改,比如这道题。
由于莫队为离线数据结构,所以我们需要考虑怎么维护这个修改操作。
但是我们可以强行修改,我们可以直接加上一维表示时间。所以就是把询问从
这样子的转移依然为
查询很简单,所以我们来考虑修改。
我们定义
但是我们要如何强行加上这个修改呢?
假设修改前位置
加上这个修改,有两种情况。第一种加入
还原这个修改,相当于把
接下来我们考虑维护查询操作:
我们在
所以就很容易做出来啦!!
code
有些时候,往莫队加一个数十分简单,可是删去一个数就非常难维护。但是有时候删去一个数非常容易,但是加入一个数就难以维护。
此时我们就要用到回滚莫队。
先按照左端点所在的块升序为第一关键字,以右端点升序为第二关键字的方式排序。
对于处理左端点在
假如询问的左右端点在同一个块里,那么直接扫描区间得出答案。
否则呢?
假如询问的右端点大于莫队的右端点,那么不断扩展右端点直至莫队区间的右端点等于询问的右端点。加入左端点和莫队的左端点不在同一个块里,那么不断扩展莫队区间的左端点直至莫队区间的左端点等于询问的左端点,然后直接回答询问。然后撤销左端点的改动,让左端点重新回到
然后复杂度依然为
先按照左端点所在的块升序为第一关键字,以右端点降序序为第二关键字的排序方法排序。
对于处理左端点在
然后和上面做差不多的事情即可,只是把加操作改为减操作。
题目让你求每次回答区间内元素权值乘以元素出现次数的最大值。
加点非常好写,直接开个桶维护个数即可。
但是删除操作不好维护,我们删掉这个点的时候我们并不知道接下来谁是最大值,所以我们使用回滚莫队。
那么回滚莫队中提到的撤销操作具体就是指在桶中减去出现次数,而不管答案是否改变。
code
bitset 可以用来常规数据结构难以维护的的判定问题。然而莫队却可以常规数据结构难以维护的区间问题。
所以我们可以把两者结合,这样可以同时利用两者的优势。
题目要求三个区间内不同时出现的数的个数,所以答案即为三个区间的长度之和 - 三个区间内同时出现的数的个数。
然后看到 unique
,直接使用 lower_bound
即可。
既然考到了区间内出现的数的个数,我们可以想到莫队。
对于每一个询问都开一个 bitset
,这个 bitset
中的每一位表示这个数是否取过。然后对于每一个小区间,我们也开一个 bitset
,这个 bitset
表示当前数是否取过,所以最后需要删除的数的个数就是三个 bitset
的交集。
但是注意到 bitset
可以开很大的范围,但是开不了
code
既然莫队一般只能处理序列上的问题,那么我们就直接强行把这整一颗数压成一个序列。
首先我们先遍历这整一颗树,加入我们遇到了 push_back(u)
,假如我们回退到 push_back(-u)
。
然后当我们在挪动指针时
add(u)
del(u)
del(u)
add(u)
这样我们就可以把这棵树变成一个序列了。
先把一棵树变为一个括号序列,然后每次添加/删除一个点,这个点的对答案的的贡献是可以在
对于重复的贡献我们直接开一个 vis
数组看这个点需不需要计算。假如
所以直接树上莫队解决即可。
还有修改,所以直接加上一维时间为即可。不会带修莫队的可以往前看。
所以就解决了。
code
上面只是把一棵树拍平,变成一个括号序列里面,这并不是真正莫队上树,那还是在序列上做的。
众所周知,莫队是基于分块的。所以在讲莫队上树时,我们需要了解树分块。
顾民思义,就是在树上分块。
具体要求如下:
属于同一块的节点之间的距离不超过给定块的大小
每个块中的节点不能太多也不能太少
每个节点都要属于一个块
编号相邻的块之间的距离不能太大
然后我们用一个 stack 来维护,假如 stack 里面的数的个数大于等于块长,那么这里面的点就成为一个新的块。
模板题
code
首先先把整棵树分块。
然后和普通莫队一样,设置左端点和右端点。但是怎么移动呢?
我们可以标记被计入答案的点,让指针直接向目标移动,同时取反路径上的点。
对于两个点的 lca 我们先不计算,等做完所有的点之后我们在此单独计算两个点的 lca 即可。
排序的方法即为按照第一个点的编号排序即可。
就是模板题。直接维护颜色数量即可。
注意移动时需要特判 lca。
code
这个神秘科技适用于
- 一个区间的贡献和区间的数有关
- 答案可以前缀和算出
二离的复杂度为
先上模板题。
发现
我们考虑使用莫队二离。
令
显然
所以对于一个区间的转移,我们可以分成两类:
一个前缀和一个其后面一个数产生的贡献,即为
区域的区间变化方式,道理是相同的。
接下来处理第二次离线的方式。
我们使用扫描线来解决。对于所有未处理的询问,我们直接对于每个
这样题目就被解决了!
我在代码中的第一种贡献并没有使用前缀和去记录,而是一个一个加的。由于指针移动的复杂度为
code
problem
solve
有一道类似的例题也贴一下:博丽灵梦,这题需要使用二维分块+只减不增的回滚莫队,有兴趣的同学可以去做一下。
1.一个简单的询问
首先我们知道
然后我们令
所以把每一个问题拆成
但是这题的统计答案和以往有些许差别,所以给个代码:
2.这是我自己的发明
和上一题差不多,只是多了一个换根操作。
我们分成三种情况来讨论。
区间就是从 1 到
画个图观察一下就知道是这个点的子树为把原来根节点的子树挖掉。
这个是不变的。
所以最后要搜哪些区间我们就知道啦!代码如下: