题意
给定长度为 \(n\) 的数列 \(a\)。
定义 \(p_k = \sum_{1 \le i, j \le k} a_i \bmod a_j\)。你需要输出 \(p_1,p_2,\ldots,p_n\)。
\(2\le n\le 10^5\),\(1\le a_i\le 3\times 10^5\),保证 \(a\) 中的元素互不相同。
初步解法
首先看一眼应该能想到显然的 \(O(n\sqrt n\log n)\) 的做法。
我们从 \(1\) 到 \(n\) 计算 \(p_k\)。对于每个算到的 \(k\),我们只要计算 \(1,2,\ldots,k-1\) 与 \(k\) 之间产生的贡献。考虑正在计算 \(i(1\le i\le k-1)\) 与 \(k\) 的贡献。
-
当 \(a_i<a_k\) 时,有 \(a_i\bmod a_k=a_i\)。我们把小于 \(a_k\) 的 \(a_i\) 累加起来,树状数组即可。考虑 \(a_k\bmod a_i\) 的值,你会发现这和 P2261 [CQOI2007]余数求和 很相似,所以我们套用它的整除分块,在计算时要用树状数组统计符合区间的个数。此部分复杂度 \(O(\sqrt n\log n)\)。
-
当 \(a_i>a_k\) 时,有 \(a_k\bmod a_i=a_k\)。我们求出大于 \(a_k\) 的 \(a_i\) 个数,树状数组即可。考虑 \(a_i\bmod a_k\) 的值,\(a_i\bmod a_k=a_i-\lfloor\dfrac{a_i}{a_k}\rfloor\times a_k\),所以我们只要计算 \(\lfloor\dfrac{a_i}{a_k}\rfloor\) 的和。考虑区间 \([a_k\times j,a_k\times(j+1)-1]\) 之间的数贡献为 \(j\),所以枚举每个区间,树状数组计算个数。由于 \(a\) 互不相同,复杂度是均摊 \(O(\log^2 n)\) 的。
总复杂度 \(O(n\sqrt n\log n)\)。提交上去!
\(\text{Time limit exceeded on pretest 7}\) \(\text{[pretests] }\)
优化解法
发现瓶颈是整除分块的 \(\sqrt n\),考虑把它替换掉。也就是考虑 \(a_k\bmod a_i(a_k>a_i)\) 的另一种算法。注意到正着做是 \(O(\sqrt n\log n)\),倒着却 \(O(\log^2 n)\)。那么能不能在计算 \(a_i\) 的时候就算好 \(a_k\) 呢?自然是可以的。
在计算 \(a_i\) 的时候,再用一个数据结构维护桶,与上面的方式类似,找到 \([a_i\times j,a_i\times(j+1)-1]\) 区间,容易发现这一段区间 \(a_i\) 的贡献是 \(-a_i\times j\),所以对这一段区间加上 \(a_i\times j\)。在到 \(a_k\) 时,查询一下 \(a_k\) 这个点的权值,在答案中减掉即可。
容易发现区间总个数仍然是 \(O(n\log n)\) 级别。这个数据结构要求支持区间加,单点查,还是树状数组即可。这部分的复杂度由 \(O(\sqrt n\log n)\) 优化到 \(O(\log^2 n)\)了。
\(\text{Accepted}\)
巨丑无比的代码:link。为了快速用了一个树状数组和一个线段树,主要内容在 main 函数中。