莫队
普通莫队
考虑一共有 \(n\) 此询问区间 \([l_i,r_i]\) 上的信息,并且能够在 \(\mathcal O(1)\) 的时间内由 \([l_i,r_i]\) 迭代出: \([l_i+1,r_i]\), \([l_i-1,r_i]\), \([l_i,r_i-1]\), \([l_i,r_i+1]\) 。
那么此时我们按照 \(len = \sqrt{n}\) 分块 ,然后以 \(l\) 所在的块为第一关键字,以 \(r\) 为第二关键字,把询问进行排序。
此时我们按照这个顺序暴力转移,可以证明时间复杂度为 \(\mathcal O(n\sqrt n)\) 。
时间复杂度证明
首先我们考虑左指针:
如果是在块内的移动,每一次最多移动 \(\sqrt n\) 次,所以一共 \(n \sqrt n\) 次。
如果是块之间移动,那么一次最懂移动 \(2\sqrt n\) ,所以一共移动 \(\left(\sqrt n \right)^2 = n\) 。
然后是右指针:
块内因为被排序过,所以总共移动最多: \(n \sqrt n\) 次。
块之间每一次最多 \(n\) 次,所以一共 \(n \sqrt n\) 次。
因此可以得到总时间复杂度为 \(\mathcal O (n\sqrt n)\) 。
一个小优化:
观察下面这个数据1:
1 2 3 4 5 | |
此时我们发现处理了第一二个之后来到了: \([2,100]\) 。
此时我们只需要操作两次就可以变成 \([4,100]\) ,但是按照现在的程序会先到 \([3,1]\) 非常的浪费。
所以我们可以考虑一个整块升序,一个降序。
代码:P1494 [国家集训队] 小 Z 的袜子
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 | |
带修莫队
对于待修莫队,其实就是增加了一个时间维度。
此时依然相邻状态可以 \(O(1)\) 转移,此时我们令块长为 \(n^{\frac{2}{3}}\) 。
然后通过下面的函数排序:
1 2 3 4 5 | |
此时可以证明时间复杂度为 \(\mathcal O(n^{\frac{5}{3}})\) 。
代码:P1903 【模板】带修莫队
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 | |
树上莫队
括号序列树上莫队
由于普通莫队只能处理序列上的问题,所以我们考虑把树转化为括号序列。
对于一次 DFS ,到达一个点时把 \(x\) 加入,走的时候把 \(x\) 加入。
然后每当我们插入和删除一个节点的时候,我们统计一个 \(vis\) 为他是否已经被计算贡献。
如果已经计算过了,就减去 \(c_x\) ,否则加上。
加上一次询问我们需要查询 \(x \to y\) 路径上的信息,那么分两种情况:
-
\(lca(x,y)=x\) 此时 \(x\) 为 \(y\) 的祖先,那么相当于查询括号序列中 \([in_x,in_y]\) 。
-
\(\texttt{otherwise}\) , 此时相当于查询 \([out_x,in_y] + lca\) 。
参考代码 P4074 [WC2013] 糖果公园
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 | |
代码细节
通过真是代码可以发现,对于第二种情况可能出现 \(out_x > in_y\) 的情况。
但是其实此时莫队是支持的,因为你不要把莫队当作一个区间,而是一个二维平面上的点: \((x,y)\) ,然后通过 \((x,y)\) 可以得到 \((x,y-1), (x,y+1), (x+1,y), (x-1,y)\) 。
按照这种理解莫队问题其实就变成了找一个路径经过所有的点,要求他们的哈夫曼路径最短。通过分块,就是莫队了。
注意: 写的时候一定要注意传的是括号序列的节点
我们发现这种办法还是有相当的局限性的, 因为他其实只是转换成序列,然后只能且介树上路径的信息。
真 · 树上莫队
%% 等待补充 %%
下面代码有一个快读。
参考代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 | |
回滚莫队
有的时候,题目我们发现是通过莫队实现 2,但是我们发现对于增加区间可以通过 \(\mathcal O(1)\) 完成,但是对于删除就不行(比如说是某一个权值的最大值)。
为了解决这个问题,我们可以选择通过更多的添加操作去代替, 比如说我们把询问的区间进行分组,然后每一组取他们的重合区间,于是都转化为扩张了。
具体来说,先正常,进行分类讨论:
-
当 \(l,r\) 在同一个块中,直接暴力进行计算。
-
当 \(l\) 所在的块 \([L,R]\) 相比上一个询问发生改变,此时令 \(l = R+1, r = R\) 。
-
当 \(l,r\) 不在一个块中:
-
扩展右端点达到查询区间(由于保证在这一区间内,右端点单调)。
-
扩展左端点 (由于每一次会重新回到 \(R+1\) ,而当前查询的每一个 \(l < R+1\))。
-
回溯左端点(这里一般是会把扩展左端点时的答案记录为 \(\texttt{Tmp}\) ,然后回溯时只需要删除其在桶中进行的操作)
-
示例代码 P5906 【模板】回滚莫队&不删除莫队
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 | |
注意
有些一些简单的最大最小值问题实际上是可以使用普通莫队做的:
比如说要维护一个区间中出现最多次的数字出现的个数(参见 P1997 faebdc 的烦恼)。
这个时候我们可以使用以下办法解决:
1 2 3 4 5 6 7 8 9 10 11 12 | |
对于维护莫队的数据结构的选择
由于在莫队中插入一共有 \(N \sqrt Q\) 次, 但是查询只有 \(Q\) 次。
所以此时如果我们选择一个修改 \(\mathcal O(1)\) ,查询 \(\mathcal O(\sqrt N)\) 的数据结构(即分块),此时时间复杂度就会被平衡为 \(\mathcal O( N \sqrt Q + Q \sqrt N)\) 。而如果写线段树等看似更快的数据结构,此时时间复杂度为 \(\mathcal O(N \sqrt Q \log N + Q \log N)\) 反而更劣。
在最后
下面是我写的时候常见的错误:
-
排序的第一个
!=写成==。 -
把
l,r和a[i].l,a[i].r搞反。