分块学习笔记

对于分块的介绍,分块入门 - 博客园,本文大致总结了一下分块入门题目.

必要补充:通常情况下分块大小取 \(T = \sqrt N\) ,可以参考2017国集论文徐明宽《非常规大小分块算法初探》.

1. P3372 【模板】线段树 1

区间加区间和的模板题,不再赘述.

AC Record

2. P3870 [TJOI2009] 开关

无脑分块,用 \(sum_{i}\) 记录块 \(i\) 中 \(1\) 的数量,再记录一下每个块的翻转,翻转两次会变成原来的值,所以我们可以用 bool 或 bitset 类型,翻转操作可以 xor 1 ,更新时散块判断注意异或上翻转标记,增减 \(sum\) ,整块 \(i\) 反转后 \(sum_i = 块长度 - sum_i\) ,同时该块翻转标记异或 \(1\) ;询问时散块暴力,整块 \(i\) 加上 \(sum_i\) 即可.

AC Record

3. P3373 【模板】线段树 2

相对于模板一增添一个数组 mul 来标记乘法,加乘分开处理,注意一下顺序.

待补代码.

4. P2801 教主的魔法

题意简化:给定 \(N\) 个数,第 \(i\) 个数为 \(a_i\) ,有 \(Q\) 次操作,每次将 \(a_{l \dots r}\) 加上 \(k\) 或者询问 \(a_{l \dots r}\) 中大于等于 \(k\) 的元素个数,\(N ≤ 10^6\),\(Q ≤ 3000\).

限制条件不好用线段树等维护,虽然 \(N\) 较大,但 \(Q\) 较小,可以分块.

对于散块直接暴力,对于整块,暴力不可取,但我们可以对整块按值排序后二分查找,总时间复杂度为 \(O(NT\log T+\frac{N^2}{T}\log T)\) ,故分块大小 \(T=\sqrt N\) (分析方法见下题),得到最优复杂度 \(O(N^{\frac{3}{2}}\log N)\).

需要注意的是,更新散块后要对所在块重新排序确保单调性,整块直接 add 标记,询问时排序后的数组非原来的下标,所以把下标也存进去一块排序,枚举时加再判断一下下标即可。

AC Record

5. P4168 [Violet] 蒲公英

参考:李煜东《算法竞赛进阶指南》0x44.

简化题意:给定 \(N\) 个数,第 \(i\) 个数为 \(a_i\) ,有 \(M\) 次询问,每次询问 \(a_{l \dots r}\) 的众数,注意,本题强制在线 ,\(n \le 40000\),\(m \le 50000\),\(a_i \le 10^9\).

在线区间众数问题,众数不满足“区间可加性”,故不可直接用线段树等维护,考虑分块。

首先,询问一个区间 \([l,r]\) ,\(pos_i\) 为 \(i\) 所在的块的编号,分两种情况:

  1. \(l\),\(r\) 在同一个块内即 \(pos_l == pos_r\) ,直接暴力枚举。

  2. 不再同一个块,分整块和散块处理,显然,众数一定来自它们之间,如果我们知道了整块的众数,再处理出现在散块中的数就能得出答案。

解法一

预处理出所有块 \(i\) 到 \(j\) 每个数的出现次数 \(cnt_{i,j}\) ,以及块 \(i\) 到 \(j\) 的众数 \(mode_{i,j}\) ,询问 \([l,r]\) 时我们将散块中的数加入 \(cnt_{l,r}\) 中,同时与 \(mode_{i,j}\) 进行比较,更新当前众数,最后将加入 \(cnt_{l,r}\) 的数减掉.

预处理时,可以发现出现次数是一个区间,完全可以用前缀和替代,用 \(cnt_{i,j}\) 表示前 \(i\) 个块中数 \(j\) 的出现次数,因为 \(a_i\) 对 \(cnt_{pos_i,a_i}\) 到 \(cnt_{块总数,a_i}\) 都有贡献,所以枚举 \(i\) ,\(cnt_{pos_i\dots 块总数,a_i}\) 都加 \(1\) ;询问时,原来的数 \(j\) 在 \(a_{l \dots r}\) 的出现次数 \(cnt_{l,r,j}\) 表示为 \(cnt_{r,j} - cnt_{l-1,j}\) ,散块的数 $j $ 加在 \(cnt_{r,j}\) 中即可;预处理众数,\(mode_{i,j}\) 一定来自 \(mode_{i,j-1}\) 和块 \(j\) 中的数,所以枚举块 \(i\) 和 \(j\) ,先令 \(mode_{i,j}=mode_{i,j-1}\) ,再统计块 \(j\) 中每个数在块 \(i\) 到 \(j\) 的出现次数,比较更新 \(mode_{i,j}\) 即可,这里可以用一个标记数组记录次数,也可以用 \(cnt\) 前缀和求.

注意,询问时不要用 memset 清数组,会贡献 \(O(n)\) 的常数,用什么清什么就行.

分块大小 \(T = \sqrt N\) 可能不是最优,先用 \(T\) 计算,块数量为 \(\frac{N}{T}\) ,预处理时间复杂度 \(O((\frac{N}{T})^2\cdot T+N\cdot\frac{N}{T})=O(\frac{N^2}{T})\) ,单次询问为 \(O(T)\) ,总时间复杂度 \(O(\frac{N^2}{T}+MT)\) ,空间复杂度 \(O(NT)\) ,\(M\) 和 \(N\) 相差不大,不妨把 \(M\) 看作 \(N\) ,则 \(O(\frac{N^2}{T}+MT)=O(\frac{N^2}{T}+NT)\) ,解方程 \(\frac{N^2}{T}=NT\) ,得分块大小 \(T=\sqrt N\) ,则总时间复杂度 \(O(N^{\frac{3}{2}})\) ,空间复杂度 \(O(N^{\frac{3}{2}})\).

AC Record

解法二

保留众数数组,用 vector 储存每个数的出现位置(下标),查询一个数在区间 \([l,r]\) 的出现次数,只需在其 vector 中二分最后一个 \(\le r\) 和第一个 \(\ge l\) 的位置再相减,时间复杂度 \(O(\frac{N^2}{T}+NT\log n)\) ,取 \(T = \sqrt{\frac{N}{\log N}}\) ,得最优时间复杂度 \(O(N^{\frac{3}{2}}\sqrt{\log N})\) ,空间复杂度 \(O(N)\) .

AC Record

上述算法,虽然空间复杂度更优,但时间复杂度更劣,考虑如何去掉 \(\sqrt{\log N}\) ,即避免二分,可以发现,在询问中判断了一个数出现次数是否大于当前众数的出现次数(设为 \(k\)),如果其在最左侧出现(左侧散块),判断出现次数 \(>k\) 就是判断它后面第 \(k\) 个同类数是否在右边界内,具体方法见代码,右侧散块同理,向左判断即可,时间复杂度 \(O(\frac{N^2}{T}+NT)\) ,取 \(T=\sqrt N\) ,得最优时间复杂度 \(O(N^{\frac{3}{2}})\) ,空间复杂度 \(O(N)\).

AC Record

6. P5048 [Ynoi2019 模拟赛] Yuno loves sqrt technology III

卡空间,把上一题的询问函数的返回值改为次数即可.

AC Record

7. P4135 作诗

简化题意:简化题意:给定 \(N\) 个数,第 \(i\) 个数为 \(a_i\) ,有 \(M\) 次询问,每次询问 \(a_{l \dots r}\) 有多少数出现偶数次,注意,本题强制在线 ,\(n,m,a_i \le 10^5\).

其实和蒲公英一样,用 \(cnt_{i,j}\) 表示前 \(i\) 个块中数 \(j\) 的出现次数,\(f_{i,j}\) 表示块 \(i\) 到 \(j\) 的答案,显然 \(f_{i,j}\) 一定来自 \(f_{i,j-1}\) 和块 \(j\) ,预处理方法同上题,枚举块 \(i\) 和 \(j\) ,先令 \(f_{i,j}=f_{i,j-1}\) ,再枚举块 \(j\) 内的数,如果一个数出现偶次,增加 \(f_{i,j}\) ,出现奇次且不是第一次则减少,这里用标记数组记录次数,用前缀和会重复计数,询问的逻辑也差不多.

此外,\(cnt\) 和标记数组都有 %2(&1),可以用 bitset 优化常数,不再实现.

AC Record

8. P3203 [HNOI2010] 弹飞绵羊

简化题意:有 \(n\) 个装置,第 \(i\) 个装置有一个弹力系数 \(a_i\) ,到达第 \(i\) 个装置会被弹到第 \(i+a_i\) 个装置,若装置不存在即 \(i+a_i>n\) 则会被弹飞,有 \(m\) 次操作,每次询问在第 \(i\) 个装置弹多少次会被弹飞,或者修改 \(a_i\) ,\(n\le2\times10^5\) ,\(m\le10^5\).

有紫握持,没有修改的话这题就很简单,倒推处理次数即可,考虑分块,预处理出装置 \(i\) 弹出所在块的次数 \(cnt_i\) 和落在的位置 \(to_i\) ,倒推分弹飞、弹出块、未弹出块的情况处理;因为是单点修改,块和块之间没有影响,直接重构所在块即可.

总时间复杂度 \(O(\frac{N^2}{T}+NT)\) ,取 \(T=\sqrt N\) ,最优时间复杂度 \(O(N^{\frac{3}{2}})\).

AC Record

9. P10590 磁力块

显然 BFS ,每次扩展队首磁石(磁力 \(p\) ,吸引半径 \(r\) ),有距离(到 \(x_0,y_0\) 的距离,用 \(d\) 表示)和质量( \(m\) )两个约束条件,考虑分块,先按距离(或质量)给数组排序,在块内再按质量(或距离)排序,存在 \(i\) 使得第 \(1\sim i-1\) 块中每个元素都有 \(d\le r\) ,可以被吸引,第 \(i + 1\) 块及以后的块都有 \(d>r\) ,不能被吸引,因为每段已经按质量重新排序,从 \(1\sim i-1\) 每段左端开始枚举,直到遇到第一个 \(m>p\) 的位置,为了避免重复,将块左端移到这个位置;对于第 \(i\) 段,暴力枚举判断每个元素.

注意,块内排序后破坏了整体顺序,整体排序后要记录每段的最大值( \(d\) 或 \(m\) ),用于判断 \(i\) 的位置.

总复杂度为 \(O(\frac{N^2}{T}+NT)\) ,取 \(T=\sqrt N\) ,得最优时间复杂度 \(O(N^{\frac{3}{2}})\).

AC Record

posted @ 2026-02-14 13:23  EvanYow  阅读(40)  评论(0)    收藏  举报