简单分块与莫队
1 - 分块
1.1 - 定义
分块是将要维护的信息分成若干块,而后通过维护整块的信息或者是块间的信息来优化算法。
1.2 - 序列分块
在序列上以线段树来类比,线段树是将序列每次对半分,最后得到一个比较完美的二叉树的结构。分块是将序列首先分成一个多叉树,根的儿子节点的儿子就直接是叶子。
1.3 - 复杂度分析
复杂度分析:假设块的大小是
分块的时候务必分析时间复杂度来取合适的块大小,不要无脑取根号。
1.4 - 分块的优点
分块在修改的时候,只有若干整块以及两个散块需要修改,在整块的处理上,其和线段树等一样,都是打标记,但是在散块上,其只需要暴力修改两个小散块的信息,整个修改过程只和散块中的元素有关,而线段树等数据结构会从叶子开始一层层往上更新,最后牵扯到整个序列信息。
分块在统计答案的时候只有整块的散块的区别,不像线段树等那般有多层结构,这使得信息难以合并的时候,分块有其特殊的用处。
1.5 - 分块思想的另一种应用
对于动态修改问题,有时候我们会遇到这样一种情况:想到了两种做法,一种可以快速修改,但查询很慢;一种可以快速查询,但是修改很慢。这种时候也可以使用分块算法进行折中处理。
2 - 莫队
2.1 - 普通莫队
对于序列上的区间询问问题,如果从
2.1.1 - 实现
离线后排序,顺序处理每个询问,暴力从上一个区间的答案转移到下一个区间的答案。
2.1.2 - 排序方法
令块长为
2.1.3 - 复杂度分析
设序列长度为
2.2 - 带修莫队
对于某些允许离线的带修改区间查询来说,我们能够对莫队算法做出一些修改,使得他支持带修改的区间查询。做法就是把莫队直接加上一维,变为带修莫队。
对于具体实现方法,我们把修改操作编号称为" 时间戳",而查询操作的时间䧸沿用之前最近的修改操作的时间䧸。跑主算法时定义当前时间戳为
2.2.1 - 实现
本质上就是查询的时候,信息在区间
2.2.2 - 复杂度分析
设块大小为
考虑第三维的移动次数,前两维分出的块个数是
第一维的移动次数显然
如果不考虑第二维的移动次数,
第二维的移动次数比较难分析,但是从最坏的角度来看,考虑第一维,第二维总是要遍历完整个序列,移动
2.3 - 树上莫队
把序列问题放到了树上。
欧拉序
:
欧拉序是在dfs
的时候,在进节点的时候把节点加入序列,出节点的时候也把节点加入序列后得到的序列。
2.3.1 - 实现
算法核心是使用树的欧拉序使得树上问题变成序列问题。
欧拉序上,刚好把链上每个点标记一遍,不在链上的点标记 LCA
并不在这段区间内,但是只有这一个点,就很好处理了。
转化之后树上问题变成了序列问题,使用普通莫队即可。
类似的,树上也有带修莫队,也是把树上问题变成序列后跑带修莫队。
2.4 - 回滚莫队
普通莫队无法快速解决最值
以下以
2.4.1 - 实现
要维护最大值,显然只能加数不能减数。所以考虑使用回滚莫队。
对于左右端点在同一块内,暴力。
剩下的按照普通的莫队排序,现在考虑左端点在同一块内的查询。其思路如下。
假设 A
和 B
是这次查询的左右端点,C
和 D
是下次的查询的,L
和 R
是当前的左右端点,竖线区分开了当前块与块外面的部分。
--A--L|R---B----
左端点设为所在块的右端点;
--A--L|----R----
扩展右端点;
--L---|----R----
右端点到达目标点后,扩展左端点;
此时我们得到了当前查询的答案;
-----L|----R----
左端点到达目标点后,撤回左端点的修改;
---C-L|----R---D
对下一个查询重复操作;
---C-L|--------R
扩展右端点;
……
2.4.2 - 复杂度分析
这样看,整个过程只有左右端点的扩展和左端点修改的撤回,就不存在删除操作了。
和普通莫队相比,复杂度并没有发生什么变化,只是常数变大了。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· winform 绘制太阳,地球,月球 运作规律
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· AI 智能体引爆开源社区「GitHub 热点速览」
· Manus的开源复刻OpenManus初探
· 写一个简单的SQL生成工具