根号数据结构复习
二月 24, 2020 · OI 算法
分块,可以看做一个度数为 $\sqrt n$,只有三层的树。
所以如果在分治结构上很难快速合并某些信息,我们就可以利用分块来做。
动态分块
0x01 经典问题
维护一个序列,支持:1.区间加;2.查询区间小于 $x$ 的数个数。
块大小为 $\sqrt {n \log n}$ 分块,可以做到 $O(m \sqrt {n \log n})$ 的复杂度。
0x02 [Ynoi2017]舌尖上的由乃
维护一个序列,支持:1.区间加;2.查询区间第 $k$ 小。
显然需要二分。块大小设置为 $\sqrt n \log n$,二分开始前把两个零散块拼起来。时间复杂度 $O(m \sqrt n \log n)$
用根号平衡来优化数据结构复杂度
0x01
维护一个序列,支持:1. $O(1)$ 单点修改;2. $O(\sqrt n)$ 区间和。
修改
查询
0x02
维护一个序列,支持:1. $O(\sqrt n)$ 单点修改;2. $O(1)$ 区间和。
维护前缀和。
修改
查询
0x03
维护一个序列,支持:1. $O(\sqrt n)$ 区间加;2. $O(1)$ 查单点。
修改
查询
0x04
维护一个序列,支持:1. $O(1)$ 区间加;2. $O(\sqrt n)$ 查单点。
差分。
修改
查询
0x05
维护一个集合,支持:1. $O(1)$ 插入一个数;2. $O(\sqrt n)$ 查询第 $k$ 小。值域与 $n$ 同阶。
修改
查询:最多跨过 $\sqrt n$ 个整块和 $\sqrt n$ 个零散的数。
0x06
维护一个集合,支持:1. $O(\sqrt n)$ 插入一个数;2. $O(1)$ 查询第 $k$ 小。值域与 $n$ 同阶。
直接维护一下整个队列,把整个队列分块。
插入一个数的时候重构一下对应的队列,如果队列超了就把最后的数丟到下一个块。
修改
查询
0x07 CodeChef Chef and Churu
给 $n$ 个数,给定 $m$ 函数,每个函数为序列中第 $l_i$ 到第 $r_i$ 个数的和。有 $q$ 个两种类型的操作:
1 x y
把序列中第 $x$ 个数改成 $y$2 x y
求第 $x$ 个函数到第 $y$ 个函数的和
把 $m$ 个函数分块。整块的可以直接维护,零散块用一个 $O(\sqrt n)$ 修改,$O(1)$ 查询的分块维护。
简单莫队算法
常见优化
- 奇偶块排序,可以快一倍左右。
- 调整块大小 $\left( n / \sqrt { 2m / 3 } \right)$ 可以快 $10\%$ 左右。
0x01 [AHOI2013]作业
给定序列,支持查询区间 $[l,r]$ 中值在 $[a,b]$ 内的不同数个数。
$n \leq 10^5,\ m \leq 10^6$。
序列离散化,跑一个莫队。拉一个 $O(1)$ 插入,$O(\sqrt n)$ 求和的分块即可。时间复杂度 $O(m \sqrt n)$。
另外显然有 polylog 做法(话说我第一反应也是 polylog(((
0x02 [Ynoi2016]这是我自己的发明
给一个树,有点权,初始根是 $1$。支持:
1 x
:将树根换为 $x$2 x y
:从 $x$ 的子树中选每一个点,$y$ 的子树中选每一个点,如果两个点点权相等则ans++
,求 $ans$。
考虑把树上换根问子树转化到序列上。每次询问有四个参数不好维护,可以差分成四个询问每次两个参数:
注意这里询问次数可能会比较多,可以考虑基数排序询问。时间复杂度 $O(n \sqrt m + m)$。
0x03 BZOJ3920 Yunna的礼物
给定序列,支持查询区间中出现次数 $k_1$ 小的数里面的 $k_2$ 小的数。卡空间。
考虑一种高维离散化技巧,以出现次数为第一关键字,数值为第二关键字排序,仍是 $O(n)$ 项的。
然后一边跑一个莫队,用一个 $O(1)$ 修改,$O(\sqrt n)$ 查询值域分块来维护就好。
时间复杂度 $O(m \sqrt n)$。
0x04 BZOJ4241 历史研究
给定序列,定义 $\operatorname{Chtholly}(l,r,x)$ 为 $x$ 在区间 $[l,r]$ 中出现次数,支持查询一个区间中最大的 $x \times \operatorname{Chtholly}(l,r,x)$。
用上一题的方法离散化后莫队维护即可。时间复杂度 $O(m \sqrt n)$。
0x04 [Ynoi2015]纵使日薄西山
给定序列,定义区间 $[l,r]$ 的贡献为其每一个子序列贡献和;定义子序列 $\alpha$ 的贡献为 $\alpha$ 内的元素去重后的和。支持查询区间贡献。
考虑数 $x$ 在长度为 $l$ 的区间中出现了 $y$ 次,贡献为:
可以分成两部分计算。第一部分是好维护的,第二部分可以把 $y$ 相同的放在一起计算。其中 $y$ 不同的至多只有 $O(\sqrt l)$ 个。
为了 $O(1)$ 算出快速幂,我们可以预处理出 $2^{1}, 2^2 , … , 2^{\sqrt n}$ 和 $2^{\sqrt n} , 2^{2 \sqrt n} , … , 2^{\sqrt n \sqrt n}$。(类似分块打表的思想)
0x05 [HNOI2016]大数
给定一个数字串和质数 $p$,支持查询这个数字串的一个子串里有多少个子串是 $p$ 的倍数。
首先处理掉 $p=2$ 或 $p=5$ 的情况。然后考虑子串 $[l,r]$ 是 $p$ 的倍数当且仅当 $s_{l-1} \equiv s_{r} \pmod p$。
离散化后等价于小 Z 的袜子。
0x06 区间逆序对
给定序列,支持查询区间逆序对个数。
Solution 1
把值域分块的结构可持久化(lxl 称为可持久化块状树),插入+可持久化的复杂度 $O(\sqrt n)$,查询的复杂度 $O(1)$。
结合经典的莫队做法可以做到 $O(n \sqrt m)$ 的时间复杂度和 $O(n \sqrt n)$ 的空间复杂度。
Solution 2
莫队二次离线:
将莫队当做是 $O( n \sqrt m )$ 次查询区间中满足特定特征的性质的数的某个信息。
如果这个信息具有可减性,可以差分。
考虑差分后变成 $O( n \sqrt m )$ 次查询前缀中满足特定特征的性质的数的某个信息。一个暴力的维护方式是:
每次我们从 $[l,r]$ 转移到 $[l,r-1]$ 或 $[l,r+1]$ 或 $[l-1,r]$ 或 $[l+1,r]$ 的时候,假设要求的是区间小于 $z$ 的数的个数。
那么就差分一下,在 $l-1$ 处打上标记 $(x,-z)$,在 $r$ 处打上标记 $(x,z)$。然后扫一遍,用值域分块维护就可以 $O(1)$ 知道每次转移的贡献了。
时间复杂度 $O(n \sqrt m)$,空间复杂度 $O(\sqrt m)$(自带两倍常数)。
由于空间常数巨大,寻址不连续,这是一个空间和时间都消耗巨大的算法。我们考虑从空间入手优化。
致谢
nzhtl1477 的讲课。