模板 - 数据结构 - 莫队算法 / Mo's Algorithm

参考一下czq的博客:https://www.cnblogs.com/JHSeng/p/10862295.html

(不带修的)莫队算法适用于什么问题?当然是不带修改的区间查询。莫队算法通过离线所有查询,然后对查询进行分块,在查询与查询之间暴力转移。

假如可以通过维护区间端点的信息,从 \([l,r]\) 的状态 \(O(1)\) 转移到 \([l \pm 1,r]\) 以及 \([l,r \pm 1]\) ,则可以从 \([l_1,r_1]\) 的状态 \(O(|l_1-l_2|+|r_1-r_2|)\) 转移到 \([l_2,r_2]\) ,莫队算法就是离线所有的这些查询,然后根据某种次序去回答这些查询,使得转移的距离比较少。注意到假如把 \(l\) 看做横坐标, \(r\) 看做纵坐标,那么查询就是二维平面上的点,总的转移距离最少的就是曼哈顿距离最小生成树。

据说在莫队的论文中指出了在最坏情况下,使用曼哈顿距离最小生成树解决这个问题的复杂度和分块是一样的,只是常数比较小,那么就不需要这么麻烦了。

分块的思路是:按每 \(\sqrt n\) 大小分为一块,左端点不在同一块中的,按左端点的从小到大排序,左端点在同一块中的,按右端点从小到大排序。比如一组 \([1,9]\) 上的查询 \([1,9],[2,4],[2,5],[3,3],[3,7],[4,5],[5,7],[6,8],[8,9]\) 按照上面的思路排序就是:

第一个块:\([3,3],[2,4],[2,5],[1,9]\) ,易知左端点总的转移数不超过 \(O(\sqrt n)\) ,右端点总的转移数不超过 \(O(n)\) 。在块与块之间的转移,左端点的转移数不超过 \(O(\sqrt n)\) (并且这里转移了会预支下一个分块的转移),右端点的转移数不超过 \(O(n)\) 。每个分块都是一样的,共有 \(O(\sqrt n)\) 个分块,所以总的复杂度为 \(O(n\sqrt n)\)

也就是说,这个算法由下面的部分组成:

1、离线查询的区间的 \(l,r,id,ans\) ,ans用来存放答案。
2、写一个按 \(l,r\) 端点排序的函数,和一个按 \(id\) 排序的函数。
3、分别写好“吐出左端点”、“吐出右端点”、“吃进左端点”、“吃进右端点”四种转移。
4、算法主体,排序查询,然后写四个while
5、重新排序查询

posted @ 2020-03-14 07:45  KisekiPurin2019  阅读(145)  评论(0编辑  收藏  举报