普通莫队
按照左端点所在块为第一关键字,右端点为第二关键字排序。然后按照暴力做即可。
设 B B B表示块的大小, n n n表示序列长度, m m m表示询问数,时间复杂度分析:
- 左端点 l l l移动次数最多是 询问数 * 块大小: O ( m B ) O(mB) O(mB)
- 右端点 r r r移动次数最多是 块个数 * 总长 O ( n 2 B ) O(\frac{n^2}{B}) O(Bn2)
于是这个的时间复杂度为 O ( m B + n 2 B ) O(mB+\frac{n^2}{B}) O(mB+Bn2).
根据基本不等式可得这个对勾函数当 B = n m B=\frac{n}{\sqrt{m}} B=mn时时间复杂度最小,为 O ( n m ) O(n\sqrt{m}) O(nm),总时间复杂加上排序就是 O ( n m + m l o g m ) O(n\sqrt{m}+mlogm) O(nm+mlogm)。
应用
百度即可。
带修莫队
经典例题:https://jzoj.net/senior/#main/show/4594
核心思想是把版本嵌套在普通莫队上,每次要注意版本对区间的影响。
即先更新当前版本对当前区间的影响,如果版本往前,那么查看是否会对当前区间有影响,有影响则更新,版本往后同理。
最后再跟普通莫队一样,在当前版本上进行左右断点的移动。
排序的方式是:以 n 2 3 n^{\frac{2}{3}} n32为一块,一共将序列分为 n 1 3 n^{\frac{1}{3}} n31块。排序第一关键字是左端点所在块编号,第二关键字是右端点所在块编号,第三关键字是对应版本。
模板类似:
for (int i = 1; i <= m; i ++) {
while (K < q[i].k) Change(++ K);
while (K > q[i].k) Change(K --);
while (R < q[i].r) Add(++ R);
while (L > q[i].l) Add(-- L);
while (R > q[i].r) Del(R --);
while (L < q[i].l) Del(L ++);
res[q[i].pos] = ans;
}
时间复杂度分析(与上面相同 B B B表示块大小,且假设 n , m n,m n,m同阶):
- 版本移动:最多有 ( n B ) 2 {(\frac{n}{B})}^2 (Bn)2个 r r r块,对于每个 r r r块 版本最多移动 n n n次,所以复杂度是 O ( n 3 / B 2 ) O(n^3/B^2) O(n3/B2).
- 右端点 r r r移动:由于移动递增,所以 r r r块相同时,最多移动 B B B次,时间复杂度是 O ( m B ) O(mB) O(mB), r r r块不同时,由于只有 n / B n/B n/B个 l l l块,所以最多移动 n 2 B \frac{n^2}{B} Bn2次,总的复杂度是 O ( m B + n 2 B ) O(mB+\frac{n^2}{B}) O(mB+Bn2).
- 左端点 l l l移动:每次最多移动 B B B个,时间复杂度是 O ( m B ) O(mB) O(mB)
当 n , m n,m n,m同阶时复杂度为 O ( n B + n 2 B + n 3 B 2 ) O(nB+\frac{n^2}{B}+\frac{n^3}{B^2}) O(nB+Bn2+B2n3),设 B = n x ∣ ( 0 < x < 1 ) B=n^x|(0\lt x\lt 1) B=nx∣(0<x<1),那么复杂度可以写成 n max ( x + 1 , 2 − x , 3 − 2 x ) n^{\max(x+1,2-x,3-2x)} nmax(x+1,2−x,3−2x),易知 x = 2 3 x=\frac{2}{3} x=32时时间复杂度最小,为 O ( n 5 3 ) O(n^{\frac{5}{3}}) O(n35).
树上不带修莫队
一般解决树上路径问题。
这个有一种很好的解决办法,即利用括号序。
对于一段路径 x , y x,y x,y,如果其 l c a lca lca不是它们中一点,则可以把路径对应到原序列上
序列中 x x x右括号位置到 y y y左括号位置中出现一次的点,再加上 x x x和 y y y的 l c a lca lca
否则假设 y y y是 l c a lca lca,那么可以对应到原序列上
序列中 y y y左括号位置到 x x x左括号位置中出现一次的点
这样就转化为普通莫队了。
树上带修莫队
根据前两个知识即可。
例题:BZOJ3052: [wc2013]糖果公园