简单分块与莫队

1 - 分块

1.1 - 定义

分块是将要维护的信息分成若干块,而后通过维护整块的信息或者是块间的信息来优化算法。

1.2 - 序列分块

在序列上以线段树来类比,线段树是将序列每次对半分,最后得到一个比较完美的二叉树的结构。分块是将序列首先分成一个多叉树,根的儿子节点的儿子就直接是叶子。

1.3 - 复杂度分析

复杂度分析:假设块的大小是 B,一共有 nB 块,整块部分的复杂的度就是 O(nB),散块部分的复杂度是 O(B),所以整体复杂度是 O(nB+B),显然 Bn 的时候最优。

分块的时候务必分析时间复杂度来取合适的块大小,不要无脑取根号。

1.4 - 分块的优点

分块在修改的时候,只有若干整块以及两个散块需要修改,在整块的处理上,其和线段树等一样,都是打标记,但是在散块上,其只需要暴力修改两个小散块的信息,整个修改过程只和散块中的元素有关,而线段树等数据结构会从叶子开始一层层往上更新,最后牵扯到整个序列信息。

分块在统计答案的时候只有整块的散块的区别,不像线段树等那般有多层结构,这使得信息难以合并的时候,分块有其特殊的用处。

1.5 - 分块思想的另一种应用

对于动态修改问题,有时候我们会遇到这样一种情况:想到了两种做法,一种可以快速修改,但查询很慢;一种可以快速查询,但是修改很慢。这种时候也可以使用分块算法进行折中处理。

2 - 莫队

2.1 - 普通莫队

对于序列上的区间询问问题,如果从 [l,r] 的答案能够 O(1)扩展到 [l1,r],[l+1,r],[l,r+1],[l,r1] 的答案,那么可以在 O(nn) 的复杂度内求出所有询可的答案,实际就是一种优美的暴力。

2.1.1 - 实现

离线后排序,顺序处理每个询问,暴力从上一个区间的答案转移到下一个区间的答案。

2.1.2 - 排序方法

令块长为 B,按照 (lB,r) 二元组升序排序,其中第一关键字是块的编号,第二关键字是右端点。

2.1.3 - 复杂度分析

设序列长度为 n,询问个数为 m。 可以发现从 (l1,r1) 转移到 (l2,r2) 的代价为他们之间的曼哈顿距离。对于每一个询问序列中的每一个块(第一关键字相同),整个块内纵坐标最多变化 n 长度 (纵坐标必然单调不减),对于每个询问,横坐标最多变化 S。一共有 nS 个块,相邻块之间转移的复杂度为 O(n),所以复杂度为 O(n2B+mB+n2B) 不妨让 n,m 同阶,取 S=n 时可达到最优复杂度 O(nn)

2.2 - 带修莫队

对于某些允许离线的带修改区间查询来说,我们能够对莫队算法做出一些修改,使得他支持带修改的区间查询。做法就是把莫队直接加上一维,变为带修莫队。
对于具体实现方法,我们把修改操作编号称为" 时间戳",而查询操作的时间䧸沿用之前最近的修改操作的时间䧸。跑主算法时定义当前时间戳为 t,对于每个查询操作,如果当前时间䧸相对太大了,说明已进行的修改操作比要求的多,就把之前改的改回来,反之往后改。只有当当前区间和查询区间左右端点、时间戳均重合时,才认定区间完全重合,此时的答案才是本次查询的最终答案。

2.2.1 - 实现

本质上就是查询的时候,信息在区间 [l,r] 上多加了一维 t,代表当前的时间戳。

2.2.2 - 复杂度分析

设块大小为 B,序列长 n,查询数 q

考虑第三维的移动次数,前两维分出的块个数是 (nB)2,第三维的移动范围是 n,所以是 n3B2

第一维的移动次数显然 qB

如果不考虑第二维的移动次数, q,n 同阶的话,复杂度是 O(n3B2+nB)B=n23 理论最优。

第二维的移动次数比较难分析,但是从最坏的角度来看,考虑第一维,第二维总是要遍历完整个序列,移动 nB×n,于此同时,第三维的修改使得第二维在块内移动 qB。不难发现这个最坏的复杂度也不会影响到 B 的选择。

2.3 - 树上莫队

把序列问题放到了树上。

欧拉序 (dfn)
欧拉序是在 dfs 的时候,在进节点的时候把节点加入序列,出节点的时候也把节点加入序列后得到的序列。

2.3.1 - 实现

算法核心是使用树的欧拉序使得树上问题变成序列问题。

欧拉序上,刚好把链上每个点标记一遍,不在链上的点标记 0 遍或 2 遍,虽然 LCA 并不在这段区间内,但是只有这一个点,就很好处理了。

转化之后树上问题变成了序列问题,使用普通莫队即可。

类似的,树上也有带修莫队,也是把树上问题变成序列后跑带修莫队。

2.4 - 回滚莫队

普通莫队无法快速解决最值 (min-max) 问题。

以下以 max 举例。

2.4.1 - 实现

要维护最大值,显然只能加数不能减数。所以考虑使用回滚莫队。

对于左右端点在同一块内,暴力。

剩下的按照普通的莫队排序,现在考虑左端点在同一块内的查询。其思路如下。

假设 AB 是这次查询的左右端点,CD 是下次的查询的,LR 是当前的左右端点,竖线区分开了当前块与块外面的部分。

--A--L|R---B---- 左端点设为所在块的右端点;
--A--L|----R---- 扩展右端点;
--L---|----R---- 右端点到达目标点后,扩展左端点;
此时我们得到了当前查询的答案;
-----L|----R---- 左端点到达目标点后,撤回左端点的修改;
---C-L|----R---D 对下一个查询重复操作;
---C-L|--------R 扩展右端点;
……

2.4.2 - 复杂度分析

这样看,整个过程只有左右端点的扩展和左端点修改的撤回,就不存在删除操作了。

和普通莫队相比,复杂度并没有发生什么变化,只是常数变大了。

posted @   view3937  阅读(48)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· winform 绘制太阳,地球,月球 运作规律
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· AI 智能体引爆开源社区「GitHub 热点速览」
· Manus的开源复刻OpenManus初探
· 写一个简单的SQL生成工具
Title
点击右上角即可分享
微信分享提示