邀请赛之前可能只会更这一次了吧QAQ
久闻莫队算法的大名,号称是“可以解决任何区间问题”的算法,今天就来稍微说一下莫队算法。
这个算法是一位名叫莫涛的国家队队长发明的算法,所以尊称为莫队算法
莫队的原版文章里面的题目有一定难度,所以可以先看一下这道例题:
Description
有n个数字,给出k,以及m个查询。
每次查询的格式是L,r,求L~r(左右包含)这个区间内数字的出现次数刚好是k的数字种数。
范围:n<=30000,k<=n,m<=30000,1<=L<r<=n,数列中元素大小<=n。
输入n,k,m,然后n个元素,然后m行查询,对于每一个询问,输出正确的答案。
Example input:
5 2 3
1 2 3 2 2
1 2
2 4
1 5
Example output:
0
1
0
关于这道题,最直观的感觉就是暴力大法好。对于每一次询问,我们都遍历L~r的所有数字,并记录出现次数为k的数字。这样的话复杂度是O(n*m)的,肯定是过不了的。
再来看一下MyZhY提出的一种方法。
设置两个指针left和right,分别指向被查询的区间左右端点L,r,然后将这两个指针不断地移动到下一个待查区间,每次移动的时候把移动的位置上的数字出现次数+1。这么做当然是可以的,但最坏情况下,每次询问移动的大小仍有可能是n次,复杂度依旧没有改变,甚至可能更慢。
但是莫队算法就是基于这种思想实现的。莫队首先给这个序列分块。什么是分块?顾名思义就是把一个完整的序列分割成若干个大小近似相同的块,维护这些块的性质就可以了。分块的时候一般是分成√n(向上取整)个块(除了最后一个块可能不完整),这样每个块里的元素近似等于√n了。我们给这些块按左端点L排序,每个块内部按右端点排序,这样一来就会有神奇的事情发生。我们仍然考虑以上的双指针思路,并分成几种情况:
1、待查询的区间完全包含在一个块内。这样直接遍历块内的所有元素就可以了,由于最多√n个元素,所以left指针最多移动√n次,而right指针则未知,最多移动n次,故复杂度是O(n√n)
2、待查询的区间跨越了不同块。则right最多也是移动√次,由于有n个块,所以最终的复杂度也是O(n√n),而left在每一次询问下也最多移动√n次,共m√n次。
由此,该算法的总复杂度为O((m+n)√n),足以应付大部分的情况了。
总结一下,莫队算法的用途是处理一系列离线的,一般是不带修改的区间查询问题。带修改的莫队的话则需要把二元组[left,right]变成三元组[left,right,x],代表询问left到right这个区间在经过x次修改后的答案。另外还有一些什么树上莫队,草丛莫队(提莫?)之类的骚操作我也不会QAQ