卡常带师
大力分块3
背景
上次做毒瘤分块还是上次:大力分块2
这次就是这次了(已经好久没做)
要问为什么隔了这么远(第六分块过了就是第十四),主要矛盾是我太弱了不会
第十四分块
「点缀光辉的第十四分块」
给定一个长度为 $n \le 5 \times 10^5$ 的序列 $a_i \le 5 \times 10^5$ , $m \le 5 \times 10^5$ 次询问,每次给定 $l, r \in [1, n]$ ,询问满足 $l \le i < j \le r$ 且 $a_i$ 是 $a_j$ 的倍数的 $(i, j)$ 有多少对
前置知识:二次离线
不懂的可以康康这个,或者自己去找找网上的资料
思路
如果你会了二次离线,那么这题的思路就还算简单了
考虑数 $x$ 对区间 $[l, r]$ 的贡献(记它为 $f(x, [l ,r])$ ),明显就是 $[l, r]$ 中 $x$ 的倍数的个数 + $x$ 的约数的个数
考虑莫队,直接维护指针移动时间复杂度过高,我们用二次离线,那么我们要解决的就是 $f(x, [1, x - 1])$ 和 $f([L, R], [1, k])$
先考虑贡献中的第一部分,即“ $[l, r]$ 中 $x$ 的倍数的个数”,我们开个桶 $b[t]$ ,表示“区间内 $t$ 的倍数的个数”,每次插入一个数时就枚举其因数跟新 $b$ ,由于一个数的因数个数小于 $\sqrt{n}$ ,所以可做
再考虑第二部分,即“ $[l, r]$ 中 $x$ 的约数的个数”,这里我们就不能用和第一部分相同的办法了,因为当 $x$ 比较小的时候, $n$ 以内的 $x$ 的倍数可能很多,枚举倍数时间不允许
但这里的“不允许”是对于计算 $f([L, R], [1, k])$ 来说的,因为它要求插入时间最大 $O(\sqrt{n})$ ,对于 $x \in [L, R]$ 询问 $f(x, [1, k])$ 是 $O(1)$ (或者 $[L, R]$ 整体询问 $O(\sqrt{n})$ );而 $f(x, [1, x - 1])$ 不会有这个问题,因为我们可以直接不枚举倍数,去枚举 $x$ 的约数,用 $O(\sqrt{n})$ 的询问解决
对 $f([L, R], [1, k])$ ,考虑平衡规划,对于 $x \ge V$ 的情况,我们直接枚举倍数,时间为 $\frac{n}{V}$
而对于 $x \le V$ 的部分,维护 $ct1[i, j]$ 表示“前 $i$ 个数中有多少个等于 $j$ ”,再维护 $ct2[i, j]$ 表示“前 $i$ 个数中有多少个含约数 $j$ ”(上面的 $j$ 都 $\le V$ ,上面定义就用了前缀和的形式,是为了方便使用)
于是, $f([L, R], [1, k])$ 就是 $\sum _{j = 1}^V ct1[k, j] \times (ct2[R, j] - ct2[L - 1, j])$ ,不难发现 $[L, R]$ 的整体复杂度为 $O(V)$ ;取 $V = \sqrt{n}$ 时间最优
另外,由于 lxl 卡空间,我们开不了二维数组,发现 $ct1/2[i, j]$ 我们的式子只用同一个 $j$ ,所以只记 $ct1[i], ct2[i]$ ,把 $j$ 放到最外层枚举,计算完 $ct1, ct2$ 后立刻去跟新贡献,然后再去存下一个 $j$
卡常
lxl 是不会让你轻松过的:
- 快读快输、
inline
- 把每个数的约数预处理了(只存 $\le \sqrt{n}$ 的)
- 卡块长,实测 $V = 50 / 30$ 时比较优秀, $B$ 对本题影响不大,直接取 $\sqrt{n}$ 即可过,但仍然实测 $B = 700 \sim 800$ 时比较优秀
- 这个很重要!我们发现如果我们还是直接给 $1 \sim n$ 每个数开一个
vector
来存二次离线的标记的话,理论时间是正确的,但在平衡规划处,由于我们是枚举 $j \in [1, V]$ ,每次计算完 $ct1, ct2$ 后就要马上枚举每个标记去跟新,而 STL 慢的和 shit 一样(主要是因为要枚举所以的 $i \in [1, n]$ 但有的 $i$ 根本没有标记,枚举它是多余的;再就是空间不连续,访问时慢的让我想死),所以要手打记录标记的数组(记得开二倍) - 向 lxl 和 luogu 祈祷(
折寿)吧
代码
ps :不知道为啥有几次提交会报一个什么“致命错误”,但再交就又过了
1 |
|
废话
强烈推荐去看 GOSICK ,对它每集很文艺的标题和男主“打完这场仗……”的 flag 印象很深
待续
但后面都好难啊……