简述
有人说莫队算法说是一个优雅的暴力,很有道理,我认为是一个优雅的分块。
作用
对于有些线段树等高级数据结构无法维护的操作,就考虑用莫队吧。
毕竟这是一个O(n^1.5)的算法,肯定功能比log的强大。
适用范围
- 离线
- 可以在知道[L,R]的情况下,快速推出[L+1,R],[L-1,R],[L,R+1],[L,R-1]的情况。
算法原理
先对询问排序,然后暴力转移每一个询问。
排序方式:先把序列分块,然后按照l的块为第一关键字,r为第二关键字进行排序。
时间复杂度
O((n+m)*n^0.5)
可以直接当做 O(n^1.5)
分析如下:
设n为长度,m为询问次数,t1为块数,t2为块长,t1*t2=n;
右端点移动:
对于左端点在一个块中时,右端点最坏情况是从尽量左到尽量右,所以右端点时间复杂度O(n),左端点一共可以在t1个块中,所以又端点总时间复杂度O(t1*n)
左端点移动:
在同一块中,移动不会超过O(t2),块之间移动不会超过O(2 * t2 ),所以最终也就是O(m*t2)。
每次询问时间复杂度一般来说是O(1),当然也要看你选择的数据结构,一共m次。
总共就是:修改时间( t1 n + t2 * m)+查询时间*m
一般情况下我们让t1=t2吧,所以就是O((n+m)*n^0.5),有时候直接认为是O(n ^ 1.5)
实际效果要好一些,因为分块的话时间是不会抵满的。
要注意修改和查询操作的时间复杂度分析。
代码
注意:
1. L,R最初是空区间,闭区间可以用下面啊的技巧表示空区间。
2. 在修改的时候L,R先改还是先动有考究(扩大区间先动,缩小区间先改),这样可以保证区间时刻都有长度,当然你的修改比较厉害可以处理负区间就无所谓了。
void modui()
{
int L=1,R=0;
for(int i=1;i<=m;i++)
{
while(L<q[i].l)blo.del(co[L++]);
while(L>q[i].l)blo.add(co[--L]);
while(R<q[i].r)blo.add(co[++R]);
while(R>q[i].r)blo.del(co[R--]);
ans[q[i].id]=blo.query(q[i].a,q[i].b);
}
}
超级简单。
坑
有空补树上莫队