这个题最开始叫根号序列的,因为这个题的三个算法分别是分块,根号分治和根号重构。
$cnt(k)$ 显然是好求的,只需要解决 $\sum [f_i=k]a_i$ 即可。
注意到 $a_i$ 是区间加的,但 $f_i$ 是单点改的,所以可以得到一个 $O(n\sqrt{n})$ 的分块做法。
维护 $s_{i,j},b_{i,j}$ 表示第 $i$ 块中数字 $j$ 的 $a_x$ 之和/个数,对于区间加,散块暴力修改 $a_i$,整块维护每个块的懒标记值 $t_i$,复杂度 $O(\sqrt{n})$。
对于区间询问 $k$,则第 $x$ 块对答案的贡献是 $s_{x,k}+b_{x,k}\times t_x$。复杂度 $O(\sqrt{n})$。
单点修改 $f$ 只需要暴力更改即可,复杂度 $O(1)$,这个过程只要维护好 $b,s$ 两个数组就行。
不难发现这个做法的空间复杂度为 $O(n\sqrt{n})$,但空间限制是 32 MB。考虑优化。
不难发现瓶颈在于 $b,s$,我们对每种数字都记录了 $\sqrt{n}$ 个信息,实际上,这种方法只适用于元素 $f_i$ 出现次数很多的情况,但若一种元素 $f_i$ 出现次数很少,直接暴力遍历也可以解决,考虑阈值分治。
先不考虑修改了 $f_i$ 的情况。若 $cnt(x)\ge \sqrt{n}$,则我们维护元素 $x$ 在 $\sqrt{n}$ 块的信息,反之用 vector 或链表维护元素 $x$ 在序列中的位置。
前者元素种类数不超过 $\sqrt{n}$,所以空间是 $O(n)$,后者显然是 $O(n)$。时间上,传统的分块做法单次查询 $O(\sqrt{n})$,而后者元素的出现次数不超过 $\sqrt{n}$,时间也是 $O(\sqrt{n})$。
考虑加入修改 $f_i$ 的操作,不难发现可能存在元素从 $cnt(x)$ 从 $\ge \sqrt{n}$ 变成 $< \sqrt{n}$。考虑定期重构,对操作分块,每 $\sqrt{n}$ 次操作就重新根号分治做初始化。
对于修改了 $f_i$ 的位置 $i$,在下一次重构前,我们不用分块也不用 vector 维护,而是把他们专门放到一个 vector 里,统计他们对每个询问的贡献,因为定期重构,这个 vector 内元素个数不超过 $\sqrt{n}$。
这一部分要注意把修改了 $f_i$ 的位置 $i$ 从原数据结构中删除,分块的直接删掉贡献,vector 可以用懒惰删除,最后总复杂度就是 $O(n\sqrt n)$。