P5398 [Ynoi2018] GOSICK 题解
第十四分块。
卡常卡到心机梗塞的一道题目。
思路
思路就是普通二次离线莫队的思路。
我们发现,题目要求的东西的贡献如果用普通莫队求解
无法做到 \(O(1)\) 的复杂度,我们可以考虑使用二次离线。
这个玩意的贡献设为 \(f(l,r)\) 。
则端点右移的贡献则为 \(f(l,r+x)\)。
差分一下:
其中 \(g(x,y)\) 表示的是第 \(x\) 位对前 \(y\) 个数的贡献。
前面那个东西可以用前缀和预处理出来,后面则可以再次离线求解。
考虑如何求解。
我们发现,题目中要求的二元组并没有 \(i \le j\) 的限制,这对于莫队来说就变得不好处理,我们可以让每个数的因数和倍数都加入贡献,这样,就有了 \(i \le j\) 的限制。
前缀和的预处理很好解决,可以直接对于每个数枚举因数,时间复杂度 \(O(n \sqrt n)\)。
对于后面的某个数对于序列的一个前缀的贡献,同样用扫描线解决。
- 求解某个数在一个前缀内的倍数个数。
这个处理比较简单,开一个桶,只要扫到一个数时,加入它的因数,询问的时候直接询问就可以了,时间复杂度 \(O(n \sqrt n)\)。
- 求解某一个数在前缀内的因数个数。
我们发现,当此时扫到的一个数比较大时,我们可以暴力跳倍数。
但是当此时扫到的一个数比较小时,暴力跳的时间复杂度就极为不优秀。
可以考虑根号分治。
当此时的数较小时,我们可以预处理出一个数组 \(pre_{x,y}\) 表示第 \(y\) 位在前 \(x\) 个数中的倍数个数。
这个的处理方法可以与1相同。
这样就得到了一个时空复杂度均为 \(O(n\sqrt n)\) 的极大常数的算法。
卡常
现在讲一下如何让大常数选手卡过这道题。
首先,空间上,\(n\sqrt n\) 的空间复杂度显然会被卡掉。
我们则可以一边处理询问,一边处理 \(pre\) 数组。
空间则变为线性。
时间上:
-
IO优化。
-
合理的开 \(long~long\)。
-
循环展开。
-
我们发现,这份代码上有很多枚举因数的 \(n\sqrt n\) 的复杂度的地方,可以把他们放在一起写。
-
二次离线的储存询问不要使用桶,使用一个数组来存,排一下序就可以了。
-
至于阀值,莫队分块可以开 \(1000\),根号分治则可以往小了开,可以在 \(30-50\) 之间是比较优秀的。

浙公网安备 33010602011771号