莫队
莫队是莫涛大佬提出的算法。
建议参阅:http://oi-wiki.com/misc/mo-algo/
0x00 前置知识
1 分块(根号算法)
2 sort
与运算符重载
0x01 算法思想
例题:给你一个数列 \(a_i\), 对于每个\(l\)和\(r\) , 输出\([l,r]\)中有多少种数 。
怎么做呢?不会
算法1:\(cnt_i\) 记录 \(i\) 出现的次数,遍历\(a_l\)到\(a_r\),\(++cnt_{a_i}\) ,最后扫描一遍,用\(ans\)计算多少个\(cnt\)不等于\(0\)。
这种做法,对于单个区间还挺好,对于多个区间就凉了(\(O(n^2)\))。
怎么优化呢?
我们可以考虑利用之前的\(ans\)。
比如下面的栗子:
比如现在知道\([l_1,r_1]\)的结果,要求\([l_2,r_2]\)的结果。
现将\(l\)往右移一位……哦少了一个\(1\)!!!
\(--cnt_{1}\)
再比如将\(r\)向右移……多了一个\(1\)。
\(++cnt_{1}\)
于是:
算法2:先得到一个区间的答案,再一步一步挪左右指针。
多好!
每次移动只有\(O(n)\)……欸那不还是\(O(n^2)\)?
但我就是觉得这算法很好,所以要优化。
所以移动指针的次数要减小。
什么好办法呢?
分块!
莫队
我们考虑讲区间分块,然后对于每个区间,以区间右端为关键字由小至大排序。
这样子对于每个块内,右端点会一直往右移。复杂度是 \(O(n)\)。
然后如果从一个块移到另一个块,也是右端点一路向左退,也是 \(O(n)\) 的。
所以复杂度是 \(O(n \sqrt n)\)。
这时候有的小朋友就要问了:
「呐,为什么不把它们都一起排序呢?✰」
问得好!
如果这样,来康康我们的左端点君。
它有可能会从不停地从左往右移动,再从右往左……这样复杂度退化成 \(O(n^2)\) 了。数据可以卡。
如果分块的话,可以对整个数列适当地打乱,不容易被卡。
没啦。
0x02 参考资料
我好菜啊。——ฅ(OωO)ฅ