莫队

莫队是莫涛大佬提出的算法。

建议参阅: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 参考资料

https://www.cnblogs.com/WAMonster/p/10118934.html

http://oi-wiki.com/misc/mo-algo/

posted @ 2020-09-12 20:11  ฅ(OωO)ฅ  阅读(146)  评论(0)    收藏  举报
"scale": 1 }, "display": { "position": "left", "width": 100, "height": 200, "hOffset": 70, "vOffset": 0 }, "mobile": { "show": true, "scale": 0.5 }, "react": { "opacityDefault": 0.7, "opacityOnHover": 0.2 } }); window.onload = function(){ $("#live2dcanvas").attr("style","position: fixed; opacity: 0.7; left: 70px; bottom: 0px; z-index: 1; pointer-events: none;") } -->