根号乱做
本文内容均设 \(n\) 和 \(q\) 同阶。
数列分块入门
数列分块入门 1
区间加,单点查值。维护加法标记,整块打标记,散块原数组暴力修改。代码
数列分块入门 2
区间加,区间查小于一个数的个数。块内二分。维护一个块内有序的对应数组,散块暴力询问,修改重构有序数组。整块内询问二分查找,修改打标记。代码
数列分块入门 3
区间加,区间找前驱。同样的块内二分,只不过不是求得不是位置而是值,判断一下无解即可。代码
数列分块入门 4
区间加,区间和。维护加法标记和区间和,散块在原数组修改,整块打标记。修改散块同时要修改整块区间和。代码
数列分块入门 5
区间开方,区间和。值域上注意到一个数最多被开方 \(6\) 次至 \(1\),此后这个位置将不会被改变。维护整块内最大值,如果小于等于 \(1\) 跳过修改,否则暴力重构。代码
数列分块入门 6
单点插入,单点查值。考虑使用动态数组维护分块,记录块的长度,插入查询都是 \(\sqrt{n} + \log \sqrt{n}\),但是块长会发生变化,插入越多越慢。设定一个阈值,块长超过后对整个序列暴力重新分块。代码
数列分块入门 7
区间加,区间乘,单点查值。维护加法标记和乘法标记,区间乘的时候也要给加法标记乘,散块暴力下放标记即可。代码
数列分块入门 8
区间推平,区间查询等于给定颜色的点数。维护覆盖标记,查询整块判断覆盖标记是否符合,无标记暴力查块,散块暴力查;推平整块打标记,散块下放标记,然后暴力修改。代码
数列分块入门 9
区间查询众数。注意到值域很大,先离散化。记录下每个数字出现的位置,预处理出整块 \(l \to r\) 的众数。最后的答案只有两种可能,一是整块的众数,而是剩下两端散块出现过的数字。所以暴力枚举散块内的数字,查询个数在前面预处理的位置上二分即可。代码
杂题
序列
时间分块。考虑只有一个数怎么做。维护其在时间轴上的状态,操作相当于区间加,查询一个区间大于等于一个数的时刻数量,这个东西分块是好维护的。
多个数依旧将时间分块,我们将每个操作按照序列维度从小到大排序。考虑序列维度上的区间加操作,相当于到达 \(l\) 这个位置给该时间到最后都加上 \(x\),走到 \(r+1\) 给该时间到最后都减去 \(x\)。类似于先离线下操作,给序列一维做扫描线,从左往右扫,然后每个操作再时间一维分块维护。
[CF551E] GukiZ and GukiZiana
序列分块。区间加,全局查两个和给定的数相等的数的最大距离。看到这个巨大神秘的时间限制,直接上分块。区间加是平凡的。查询操作对每个块维护一个 \(\text{map}\) 或者 \(\text{set}\),查询这个块里面是否存在这个数,如果存在就从头和尾遍历一遍这个块,遇到第一个合法的元素就跳出,更新答案。
哈希冲突
根号分治。分治模数的大小。预处理模数小于 \(\sqrt{n}\) 的和,复杂度为 \(\mathcal O(n \sqrt{n})\),查询 \(\mathcal O(1)\)。模数大于 \(\sqrt{n}\) 的最多只有 \(\sqrt{n}\) 个位置,直接暴力计算,复杂度为 \(\mathcal O(\sqrt{n})\)。
[CF797E] Array Queries
根号分治。分治 \(k\) 的大小。当 \(k < \sqrt{n}\) 的时候递推预处理,\(k \geq \sqrt{n}\) 的时候暴力模拟。
[CF1270F] Awesome Substrings
给定一个 \(01\) 序列,求 \(d Sum_{l \to r} = r - l + 1\) 的数量,求出前缀和数组 \(S\) 后转化成 \(d (S_r - S_l) = r - l\)。
考虑根号分治 \(d\)。定义一个阈值 \(T\),当 \(d \le T\) 的时候,转化柿子变成 \(d S_l - l = d S_r - r\),直接枚举 \(d\) 和 每一个位置,看有多少个 \(d S_i - i\) 是相同的,若有 \(k\) 个相同的,则对答案的贡献为 \(\binom{k}{2}\)。这部分复杂度为 \(\mathcal O(Tn)\)。当 \(d > T\) 的时候,注意到 \(S_r - S_l \le \frac{n}{T}\),考虑直接枚举 \(S_r - S_l = k (1 \le k \le T)\),先枚举 \(l\) 固定住,符合条件的 \(r\) 必然是一段区间,设 \(L \le r \le R\),\(L,R\) 可以用双指针一遍扫,然后计算这段区间 \([L-l,R-l]\) 有多少个 \(k\) 的倍数即为答案。注意需要特判舍掉枚举出来的 \(d \le T\) 的情况。这部分复杂度为 \(\mathcal O(\frac{n^2}{T})\)。
总复杂度 \(\mathcal O(Tn + \frac{n^2}{T})\),\(T\) 取 \(\sqrt{n}\) 时最优,为 \(\mathcal O(n \sqrt{n})\)。
[CF348C] Subset Sums
根号分治。注意到元素总共不超过 \(10^5\),这意味着元素个数多于 \(\sqrt{n}\) 的集合不会超过 \(\sqrt{n}\) 个。考虑根号分治。记元素个数超过 \(\sqrt{n}\) 的集合为大集合,少于的为小集合。预处理出每个大集合的元素和,以及每个集合和每个大集合的交集。需要维护大集合的元素和,以及大集合修改操作的贡献标记。对于小集合的修改,首先遍历集合中元素,直接在原序列中修改;然后根据和每个大集合的交集元素数量 \(\Theta (1)\) 计算出每个大集合增加了多少,修改元素和。时间复杂度 \(\mathcal O(\sqrt{n})\)。对于大集合的修改,直接在这个集合的修改标记上加上即可。时间复杂度 \(\Theta (1)\)。对于小集合的查询,先遍历原序列求出小集合做出的贡献以及原本的和;然后根据和每个大集合的交集元素数量以及大集合的修改标记算出小集合的和应该再增加多少。时间复杂度 \(\mathcal O(\sqrt{n})\)。对于大集合的查询,\(\Theta (1)\) 询问记录的大集合元素和;然后根据然后根据和每个大集合的交集元素数量以及大集合的修改标记算出该大集合的和应该再增加多少。时间复杂度 \(\mathcal O(\sqrt{n})\)。综上所述,总时间复杂度为 \(\mathcal O(n \sqrt{n})\)。轻微卡空间,注意变量类型。
[CF617E] XOR and Favorite Number
普通莫队。考虑前缀异或和,记 \(s_i = s_1 \oplus s_2 \oplus \cdots \oplus a_i\),从而将问题转化为多次给定询问 \(l,r\) 求 \(l-1 \le l' \le r' \le r\) 中有多少个 \(s_{l' - 1} \oplus s_r = k\)。维护区间每个 \(s_i \oplus k\) 的值的个数,可以莫队操作。
[CF1619H] Permutation and Queries
根号分治。感觉这辈子都想不到,太妙了。记录每个位置大跳 \(B\) 步能到哪以及前驱后继,修改操作可以 \(\Theta(1)\) 交换前驱后继,注意到只会有 \(\dfrac{n}{B}\) 个位置向后大跳的位置会改变,暴力模拟修改即可。询问就是能大跳就大跳,不然就后继一步一步挪。这样复杂度是 \(\mathcal O(q(B + \dfrac{n}{B}))\),取 \(B = \sqrt{n}\) 最合适,最后为 \(\mathcal O((n + q) \sqrt{n})\)。