莫队是一个非常神奇的东西,据说可以解决所有的离线区间查询问题,使用的前提是区间查询是离线的,不能有更新操作,并且[l,r]区间的查询可以在O(1)时间复杂度内转移到区间[l,r+1] [l,r-1] [l-1,r] [l+1,r],思路很简单,首先对n个离线查询的操作进行分块,采用平方分割的方法分成n^0.5块,对所有的的离线操作区间排序,排序规则是根据其左端点是第几块来排,同一块则根据右端点排序。
更确切的说,该算法存在的意义是减少原本暴力过程中大量的重复计算,举个例子
四个区间
[1,100],[2,4],[3,99],[4,6]
如果贪心的话,应该是根据左端点 (或右端点) 排序,假设根据左端点排序,就是上述情况,这种情况下,如果定义两个指针指向当前区间的左右端点(初始指向第一个),对于第一个区间,左端点不动,右端点移动n次,第二个操作,右端点从末尾移动到起始位置,操作接近n次,依次类推,其实总的时间消耗并没有减少,但是莫队算法中也涉及到了排序,这个排序的方式比较奇特,它是把区间按块分开,对块排序,一个块内只有n^0.5个元素,在同一个块中操作的时间消耗小于在整个区间操作,最后得到的是一个大致左端点上升的序列,相当于是将上述区间的顺序变为
[2,4] [4,6] [3,99] [1,100]
显然比之前少了许多的重复计算
具体实现过程
- 先对区间分块
- 对区间排序
//每根号n个分成一块
block = sqrt(n);
for(int i = 1; i <= n; i++){
pos[i] = (i - 1) / block + 1;
}
//排序法则
bool cmp(p a, p b){
if(pos[a.l] == pos[b.l])return a.r < b.r;
return pos[a.l] < pos[b.l];
}
对于区间的存储,可以定义一个结构体,包括三个变量,区间的左右端点,和该区间是第几个离线操作的区间,最后一个变量的意义是保证我们可以正确的输出对应的结果