根号数据结构
参考资料
莫队
就是对询问重排,按照一定的顺序去处理,移动询问区间的指针,就可以使得复杂度大大降低。
一般都是处理离线询问,在线的话是一种科技,但是在比赛中不太可能考。
普通莫队
形式
序列长度为 \(n\),\(q\) 次询问,每次询问 \([l,r]\) 中的一些东西。
解题思路
对询问重排,分块,先按照左端点按照所在块编号排序,在按照右端点位置排序,每次移动左右指针。块长在 \(n,q\) 同阶下取 \(\sqrt n\),否则是 \(n\over \sqrt q\)时间复杂度 \(O(n\sqrt q)\)。
时间复杂度
分析一下时间复杂度,\(B\) 是块长:
- 左端点移动次数:每次移动量 \(O(B)\),总共是 \(O(qB)\)。
- 右端点移动次数:块内有序,最多是从 1 到 \(n\),所以块内是 \(O(n)\),有 \(n\over B\) 个块,总共是 \(O({n^2\over B})\)。
总时间复杂度 \(O(qB+{n^2\over B})\),最优取 \(B={n\over \sqrt q}\),时间复杂度 \(O(n\sqrt q)\)。
带修莫队
形式
在普通莫队的基础上,加上修改操作(次数为 \(m\)),每次操作可能是询问也可能是修改。其实就是三维莫队。
解题思路
相当于加上时间一维,我们这次先按照左端点分块排,在按照右端点分块排,最后按照时间分块排,维护三个指针移动。
时间复杂度
- 左端点:\(O(qB)\)。
- 右端点:\(O(qB+{n^2\over B})\)。
- 时间 \(t\):左右端点组成的二元组数量 \(O({(n\over B)}^2)\),\(t\) 复杂度 \(O({mn^2\over B})\)
当 \(n,m,q\) 同阶,通常取 \(B=n^{2\over 3}\),时间复杂度 \(O(n^{5\over 3})\)。
奇偶优化
无法直接改变渐进复杂度,但可以大大优化常数,有效减少了 \(r\) 跳跃幅度过大的问题。
拿普通莫队举例:
如果左端点块的编号为奇数,\(r\) 按照升序排列,否则 \(r\) 按照降序排列。
在比如说带修莫队,或者高维莫队:
先按照第一维的奇偶性,决定第二维的排法,再按照第二维的奇偶性决定第三维,以此类推:
参考 cmp 代码
sort(q + 1, q + Q + 1, [&](Query a, Query b) {
int al = a.l / B, bl = b.l / B;
int ar = a.r / B, br = b.r / B;
if (al != bl) return al < bl;
if (al & 1) {
if (ar != br) return ar > br;
} else {
if (ar != br) return ar < br;
}
if (ar & 1) return a.t > b.t;
else return a.t < b.t;
});
回滚莫队/不删除莫队
不能用奇偶优化。
形式
与普通莫队类似的询问区间,\(q\) 次询问,但是询问的内容不好维护删除操作,或者有些不好维护增加操作,反正有一种操作好维护,另外一种不好维护。
比如说让你查询区间出现最多的颜色数,这个很好增加,因为你只需要考虑打擂台即可。但是删除不好做,因为你还要知道次大值甚至更后的。
解题思路
这时候就要用到回滚莫队了,与普通莫队类似的,将询问离线下来,分块,并且得到每个块的左右端点 \(L_i,R_i\)。
我们按照块的编号从左到右扫来考虑问题,我们该怎么保证只能增加的操作呢?
我们先考虑如果一个询问区间的左右端点都在当前块,那么可以暴力统计,单次时间复杂度是 \(O(\sqrt n)\)。如果不在当前块,也就是 \(r>R_i\) 呢?
现在右端点是递增的,这很好的保证了只增加。然而左端点可能会在块内乱跳。我们该如何处理呢?我们对于每个要统计的询问区间,维护双指针 \(l,r\),\(r\) 就像普通莫队那样一直向后扩展,而 \(l\) 每次都回到当前块的右端点过一个位置,即 \(R_i+1\),\(r\) 初始化为 \(R_i\),初始的的时候统计区间是空的。
每次继续尝试扩展 \(r\),记录,再去扩展 \(l\),统计答案。然后 \(l\) 跳回 \(R_i+1\),而当前维护的答案也回到还没扩展 \(l\) 之前的记录值。
复杂度
复杂度与普通莫队一样,左端点的移动量也是 \(O(\sqrt n)\),只不过可能常数大一点。
树上莫队
把树排成序列,用莫队来做。
trick
- 莫队的奇偶优化,还有左右端点移动时,一定要先扩张在收缩。
题目
P14420 [JOISC 2014] 历史的研究 / Historical Research \(\checkmark\)
题目大意
给定长度为 \(N\) 的序列 \(X_i\),表示每天发生的事件种类。共 \(Q\) 次询问,每次给出区间 \([L,R]\)。对每种在区间中出现的事件种类 \(A\),定义其重要度为
其中 \(T_A\) 是 \(A\) 在区间 \([L,R]\) 中出现的次数。对每个询问,求区间内所有事件种类的重要度最大值并输出。
\(1\le Q,N\le 10^5\),\(1\le X_i\le 10^9\),\(1\le L\le R\le N\)。
解题思路
回滚莫队板子题。
一定要仔细注意边界处理。看看自己的代码。

浙公网安备 33010602011771号