莫队二次离线学习

例题:给定 $n$ 项数列 ${a_k}$, 回答 $q$ 个问题,第 $i$ 个问题求数列的第 $l_i$ 项到第 $r_i$ 项组成的子数列的逆序数。可以离线回答。$n, q$ 同阶。

这一经典问题有一个显然的解答——离散化后莫队树状数组,时间 $O(nsqrt nlog n)$, 空间 $O(n)$.

实际上该问题已经有很简便的做法做到 $O(nsqrt n)$ 时间、$O(n)$ 空间,也就是莫队二次离线

我们注意到传统莫队算法的每个变更询问都形如求 $a[x:y]$ 中小于/大于 $a_i$ 的项数,直接使用树状数组维护,查询和修改都是 $O(log n)$. 但是如果将 $a[x:y]$ 拆成 $a[:y]$ 和 $a[:x]$ 的差,并将这些询问全部离线处理,我们就注意到:数列总共只有 $O(n)$ 个前缀,但是却要应付 $O(nsqrt n)$ 组询问。因此,这个二维偏序问题要达到理论最优的复杂度,应该 $O(sqrt n)$ 插入、$O(1)$ 查询,离散化后对值分块维护不超过该值的数的个数即可。如此,我们已有一个 $O(nsqrt n)$ 时空的算法。

但是我们注意到,当端点移动时,构成其差量的两项偏序具有明显规律——一项是求 $a[:x]$ 中小于/大于 $a_x$ 的项数,一项是求 $a[:y]$ 中小于/大于 $a_x$ 的项数。前者总共只有 $O(n)$ 种(学的时候这里卡了老久),预处理后查询即可,根本不需要存入询问。后者的查询关于同一前缀,且查询的另一参数是数列的一段。这启发我们把查询的数值在数列中的起讫位置而不是数值本身记入询问,因为这样的移动只有 $O(n)$ 次,我们也只需要开 $O(n)$ 内存存储询问。因此,算法的空间复杂度优化到了 $O(n)$.

实现细节上,我把大于和小于都转化成小于等于,用六元组 $(x, l, r, c, k, T)$ 来描述一个询问:当 $y$ 取遍 $a[l:r]$ 时,$a[:x]$ 中小于等于 $y+c$ 的项数之和,并将其乘以符号 $k$ 计入第 $T$ 次“二次离线询问”的答案。原询问的答案即为二次离线询问的答案的前缀和。

原文地址:https://www.cnblogs.com/nealchen/p/11603318.html