普通莫队
假设我们已知区间 l,r,需要计算的区间为 L,R,由于 l 和 r分别只能单步转移,所以需要的时间复杂度为| L-l |+|R-r|。相当于把两个区间分别看成是平面上的两个整点p1(l,r)和p2(L,R)两点之间的转移开销为两点之间的曼哈顿距离。连接所有点的最优方案为一棵树,那么整体的时间复杂度就是这棵树上所有曼哈顿距离之和。
于是乎最优的复杂度肯定是这棵树是最小生成树的时候,也就是曼哈顿距离最小生成树。
我们先对序列分块,然后以询问左端点所在的分块的序号为第一关键字,右端点的大小为第二关键字进行排序,按照排序好的顺序计算,复杂度就会大大降低。
- 分块相同时,右端点递增是的,分块共有个,复杂度为
- 分块转移时,右端点最多变化,分块共有个,复杂度为
- 分块相同时,左端点最多变化,分块转移时,左端点最多变化,共有个询问,复杂度为
有总时间复杂度就是
带修改莫队
普通的不带修改的莫队算法要把每个询问带上两个关键字排序,现在待修改的莫队算法要带上三个关键字排序。 这也是主要思想,和普通的莫队一样很简单的思想。 原本的莫队是[l,r]向连边推,现在带修改那么就设设三元(l,r,x),x为已经操作了x次修改,可以向(l±1,r,x),(l,r±1,x),(l,r,x±1)推,原理一样。
在进行修改操作的时候,修改操作是会对答案产生影响的(废话)
那么我们如何避免修改操作带来的影响呢?
首先我们需要把查询操作和修改操作分别记录下来。
在记录查询操作的时候,需要增加一个变量来记录离本次查询最近的修改的位置
然后套上莫队的板子,与普通莫队不一样的是,你需要用一个变量记录当前已经进行了几次修改
对于查询操作,如果当前改的比本次查询需要改的少,就改过去,反之如果改多了就改回来
说的听绕口的
比如,我们现在已经进行了3次修改,本次查询是在第5次修改之后,那我们就执行第4,5次修改
这样就可以避免修改操作对答案产生的影响了
时间复杂度
以下内容借鉴自洛谷题解
原版莫队是将区间(l,r)视为点(l,r),带修改的即加一维时间轴(l,r,t)
对于t轴的移动可以保存每次修改,如果修改在(l,r)间则更新
分块方法可以参照原版莫队,先将l分块,再将r分块,同一块的按t排序
块大小为(n*t)^(1/3)可以达到最快的理论复杂度(n^4*t)^(1/3)证明如下
设分块大小为a,莫队算法时间复杂度主要为t轴移动,同r块l,r移动,l块间的r移动三部分
t轴移动的复杂度为n*n*t/a*a,r块l,r移动复杂度为n*a,l块间的r移动复杂度为n/a
三个函数max的最小值当a为(n*t)^(1/3)取得,为(n^4*t)^(1/3)
例题
bzoj 2038 小z的袜子 (普通莫队)
题意:一个n个数的数组,m次询问,问选 l,r 范围内二个相同的数的概率,
思路:维护区间数的个数,莫队模板题,没什么好说的
codeforces 617E XOR and Favorite Number(普通莫队)
题意:给n个数的数组,m次操作,每次操作求l,r范围内有多少对i,j a[i]^a[i+1]^.....^a[j]==k;
思路:先求出a的前缀异或和,a[j]^a[i-1]==k说明就有一组满足,用莫队维护每个数在l,r内的数量,答案就是a[x]*a[x^k],当然要对k=0特殊讨论,注意 当a[i]^a[j]==k时,其实区间最小可以为 i+1,j
bzoj 2120 数颜色 (带修改莫队)
题意:一个n个数的数组,m次操作 操作1求区间l,r有多少个不同的数,操作2 单点更新
思路:维护区间数的个数,带修改莫队模板题,没什么好说的
CodeForces 940F Machine Learning(带修改莫队)
题意:给一个n个数的数组,m次操作 操作1求区间l,r cx 表示区间l,r x的出现次数,求mex(c0,c1..),操作2 单点更新
思路:先离散化 a[i],和操作2的 val, 用数组num1记录区间l,r数的出现次数,数组num2记录区间l,r出现 num1出现的次数
既 :假设区间 l,r有个数为x 那么num1记录x的数量, 假设num1[x]=y 那么num2记录y的数量 暴力找答案,然后带修改套莫队算法模板即可