寒假集训 根号算法
根号分类讨论
HDU7226
给出一个排列 \(p\),一个完全图 \(i,j\) 的边权为 \(|i-j|\cdot|p_i-p_j|\),求最小生成树。
构造一条链,连接 \(i,i+1\),这样连的边小于 \(n\)。那么跑 Kruskal 求出来的最小生成树的边也小于 \(n\)。那么 \(|i-j|,|p_i-p_j|\) 必有一个小于 \(\sqrt n\),分别枚举 \(|i-j|<\sqrt{n},|p_i-p_j|<\sqrt{n}\) 求出小于 \(n\) 的边跑 Kruskal,复杂度 \(O(n\sqrt n)\)。
分块
最常见的根号算法,可以维护一些线段树维护不了的信息,线段树能维护的需要进行信息合并,分块不需要。但是分块需要支持标记合并。
通常实现时,区间修改整块打标记,散块下传标记后暴力重构,询问整块查表,散块暴力扫描统计答案。
一次操作 \(O(B+\frac{n}{B})\),\(B=\sqrt n\) 最优,为 \(O(\sqrt n)\)。
分块,维护 \(b_i,p_i\) 分别表示一个点跳出块的步数和跳出的位置。
一个序列 \(a\),两种操作:\(add\ l\ r\ v\) 区间加,\(count\ l\ r\) 询问 区间 \([l,r]\) 有多少 \(a_i\) 仅由 \(4,7\) 组成。\(1\leq N,M\leq 10^5,1\leq a_i\leq 10^4,\leq v\leq 10^4\)。
发现 1e4 内满足仅由 \(4,7\) 组成的很少,有 \(30\) 个,询问时枚举 \(30\) 个数。
那么操作二变成询问区间内有几个数等于 \(x\)。每个块开一个桶维护即可。
区间加,区间 rank。
每块维护排序的数组,询问二分答案,求区间有多少数小于等于 \(mid\)
修改 \(O(B\log n+\frac{n}{B})\),查询 \(O(\frac{N}{B}\log^2n)\)。块长取 \(\sqrt{n\log n}\) 最优。复杂度 \(O(n\log n\sqrt{n\log n})\)。
当题目没有修改且询问的东西难维护,可以这么处理:
\(ans_{i,j}\) 为第 \(i\sim j\) 块的答案,\(sum_{i,j}\) 为前 \(i\) 块关于值 \(j\) 的某些信息。查询时用 \(ans\) 得到初步答案,用 \(sum\) 处理散块。代表性问题为区间众数。
一个序列,多次询问区间加权众数。
\(ans_{i,j}\):第 \(i\sim j\) 块的加权众数。
\(sum_{i,j}\):前 \(i\) 块 \(j\) 的出现次数,用来处理散块。
查询时先取 \(ans\) 为初步答案,遍历散块更新答案,一个数的出现次数为散块的出现次数加上整块的出现次数,用 \(sum\) 查询。
一个序列,多次询问区间出现偶数次的元素个数。
同上。\(ans\) 得到初步答案,\(sum\) 更新散块。
BZOJ3744
一个序列,多次询问区间逆序对数。
\(sum_{i,j}\):前 \(i\) 块不大于 \(j\) 的数出现次数;
\(ans_{i,j}\):第 \(i\sim j\) 块逆序对数。
莫队
离线算法,通过对操作执行顺序进行修改可以达到 \(O(n\sqrt n)\)。需要能快速从 \([l,r]\) 转移到 \([l-1,r],[l+1,r],[l,r-1],[l,r+1]\)。
对序列分块,以询问左端点为第一关键字,右端点为第二关键字。然后维护 \(l,r\) 一边移动一边更新答案。
JZOJ3568
裸的莫队,维护一个桶。
一个序列,多次询问区间 \(\operatorname{mex}\)。
首先莫队维护一个桶。查询 \(\operatorname{mex}\) 时不能暴力遍历桶,可以再对这个桶使用分块,即莫队套值域分块,每个块维护一个种类数。询问时找到最小的有数字不出现的块(种类数小于块长),再遍历块内找 \(\operatorname{mex}\)。
一个序列,多次询问区间 \([l,r]\) 中数值介于 \([x,y]\) 之间的个数。
同上,莫队套值域分块。
树上莫队:
记录括号序,设节点在欧拉序的两次出现为 \(a,b\)。
不妨设 \(a_u<a_v\)。若 \(u\) 是 \(v\) 的祖先,那么 \(u\rightsquigarrow v\) 在欧拉序上为 \([a_u,a_v]\)。否则 \(u\rightsquigarrow v\) 对应 \([b_u,a_v]\)。这个区间不包含 \(lca(u,v)\),需要额外加上。
如果一个点在区间内出现两次,说明这个点进了又出了,不在路径上。那么在路径上的点会出现一次,不在的点会出现两次。
一棵树,多次询问路径 \(u\rightsquigarrow v\) 上的点权种类数。
树上莫队板子。
带修莫队:
加一维 \(x\) 时间戳表示经过了多少修改,与 \(l,r\) 指针一样转移。
分块方法类似,三个关键字。块长取 \(n^{\frac{2}{3}}\) 时有最优复杂度 \(O(n^\frac{5}{3})\)。
如果莫队有 \(d\) 维,块长取 \(n^{\frac{d-1}{d}}\) 最优,复杂度 \(O(n^\frac{2d-1}{d})\)。
带修莫队板子。
定期重构
对于修改操作不是马上更新,而是保存一些修改操作,当修改操作达到一个值 \(C\) 时暴力更新维护信息并清空保存的修改,共 \(O(\frac{NM}{C})\)。对于查询,先假设没有修改,再枚举修改对询问产生的影响,共 \(O(NC)\)。一般 \(C\) 取 \(\sqrt m\) 最优。
一个序列,需要支持插入和查询某个位置的值。
不会平衡树可以定期重构,每 \(\sqrt m\) 个修改就 \(O(n)\) 建出序列;每个查询 \(O(\sqrt m)\) 计算保存的修改对查询的影响。
另一道题:
一个 \(n\times m\times h\) 的三维空间,每次操作在空间中加点或询问一个位置到最近点的曼哈顿距离。
维护一个 \(dis\) 表示每个位置的最近点。
重构:将新增的点与已有点混合跑 bfs;
询问:枚举新增点的曼哈顿距离与 \(dis\) 取 \(\min\)。
根号分治
原始的根号算法。对于一道题,想两种暴力做法,一种块长在分子,一种块长在分母。那么以根号为分界线分治,就能让两种方法的复杂度平衡。
一道题:
一个 \(01\) 串 \(S\),多次询问,给出一个 \(01\) 串,求 \(S\) 有几个子串 \(0\) 和 \(1\) 的个数与这个串相同。
设分治的阈值为 \(B\)。
记询问的串长和为 \(P\),暴力枚举 \(S\) 中长度相同的串,总共 \(O(N\frac{P}{B})\);
预处理 \(f_{i,j}\) 表示长度为 \(i\),有 \(j\) 个 \(0\) 的数量。复杂度 \(O(NB)\)。
总共 \(O(N(B+\frac{P}{B}))\),\(B=\sqrt P\) 时平衡,复杂度 \(O(N\sqrt P)\)。
整除分块
用来求求和符号内带有 \(\lfloor\frac{n}{i}\rfloor\) 的式子,例如 \(\sum_{i=1}^n f(i)\lfloor\frac{n}{i}\rfloor\)。
不难发现 \(\lfloor\frac{n}{i}\rfloor\) 的取值是成段的。当 \(i\leq\sqrt n\),取值不超过 \(\sqrt n\) 种,当 \(i>\sqrt n\),\(\lfloor\frac{n}{i}\rfloor\leq\sqrt n\)。因此总取值是根号级别的。
假设已知每一块左端点 \(l\) 和右端点 \(r\) 计算贡献,那么式子就变成 \(\sum_{(l,r)}[\sum_{i=l}^rf(i)]\lfloor\frac{n}{i}\rfloor\)。
关于 \(r\):
因此 \(r=\lfloor\frac{n}{\lfloor\frac{n}{l}\rfloor}\rfloor\)。
求 \(\sum_{i=1}^n k\bmod i\)。
推式子:
之后是整除分块板子。
[[数据结构]]

浙公网安备 33010602011771号