莫队
0. 离线与在线
在线算法一般是 预处理 + 输出。离线算法一般是 输入 + 一起处理。
一个典型的例子是求 LCA,有两种方法:倍增、Tarjan。
倍增是预处理所有节点的 \(2^k\) 祖先,然后每次暴力跳。
Tarjan 是输入完所有数据,再全部放在树上处理。
1. 普通莫队
1.1 莫队主体思想
如果 \([l,r]\) 能够 \(O(1)\) 扩展到 \([l-1,r],[l+1,r],[l,r-1],[l,r+1]\),且不强制在线,那么可以使用分块。
莫队 = 分块 + 暴力 = 优雅的分块 = 优雅的优雅暴力。
1.2 分块部分与查询顺序
为了保证时间复杂度,需要对原序列进行分块。
定义一次询问的块在 \(\left\lfloor\dfrac{l}{kc}\right\rfloor\)。
我们对询问的定义是这样的:
struct ask{
int l,r,id;
bool operator < (const ask A) const {
if(l/kc!=A.l/kc) return l<A.l;
if((l/kc)&1) return r<A.r;
return r>A.r;
}
};
如果两个块不在一块,考虑按左端点排序。这样可以保证 \(l\) 大体上从左往右递增。
如果两个块在一块,使用奇偶性排序。
详见 oi.wiki。
1.3 处理答案
处理答案的常用方法是从已知区间 \([l,r]\) 扩展到 \([L,R]\)。这一部分较简单:
for(int i=1;i<=q;i++)
{
ask q=b[i];
while(l>q.l) update(--l,1);
while(r<q.r) update(++r,1);
while(l<q.l) update(l++,-1);
while(r>q.r) update(r--,-1);
// 处理答案
}
update 部分较为灵活:如果 \(x\) 位置出现/失去,会对答案造成怎样的影响。
1.4 时间复杂度分析
左端点在每个块时间复杂度为 \(O(n)\)(左端点移动具有单调性),共 \(O(\sqrt n)\) 块,时间复杂度 \(O(n\sqrt n)\)。
右端点在每个块时间复杂度为 \(O(n)\),共 \(O(\sqrt n)\) 个块,时间复杂度 \(O(n\sqrt n)\)。
所以莫队时间复杂度 \(O(n\sqrt n)\)。
1.5 求区间和
经典莫队题?
考虑移动一次区间对答案的贡献,update 如下:
void update(int pos,int Sign)
{
Count+=Sign*a[pos];
}
1.6 数列找不同
经典莫队题。
考虑数当前区间的颜色数,update 如下:
void update(int pos,int Sign)
{
cnt[a[pos]]+=Sign;
if(cnt[a[pos]]==1&&Sign==1) Count++;
if(cnt[a[pos]]==0) Count--;
}
1.7 小 Z 的袜子
经典莫队题。
我们知道一种颜色的袜子的贡献为 \(\dfrac{1}{2}cnt(cnt-1)\),所以 update 如下:
void update(int pos,int Sign)
{
Count-=cnt[a[pos]]*(cnt[a[pos]]-1)/2;
cnt[a[pos]]+=Sign;
Count+=cnt[a[pos]]*(cnt[a[pos]]-1)/2;
}
2. 带修莫队
2.1 不能带修?时间维!
从普通莫队到定义 \([l,r,time]\)。
如果 \([l,r,time]\) 可以 \(O(1)\) 变换成 \([l-1,r,time],[l+1,r,time],[l,r-1,time][l,r+1,time],[l,r,time-1],[l,r,time+1]\),那么可以使用带修莫队。
带修莫队的时间复杂度一般为 \(O(n^{2/3}m^{2/3}t^{1/3})\) 也就是常说的 \(O(n^{5/3})\)。
2.2 带修莫队
第一关键字是左端点所在块,第二关键字是右端点所在块,第三关键字是时间。
块长设置 \(O(n^{2/3})\)。
2.3 【模板】树状数组
2.4 「国家集训队」数颜色/维护队列
3. 二维莫队
3.1 二维莫队的原理
二维莫队的思路同带修莫队。
考虑维护 \([l_1,r_1,l_2,r_2]\),如果能 \(O(1)\) 转移 \([l_1-1,r_1,l_2,r_2],[l_1+1,r_1,l_2,r_2],[l_1,r_1-1,l_2,r_2],[l_1,r_1+1,l_2,r_2],[l_1,r_1,l_2-1,r_2],[l_1,r_1,l_2+1,r_2],[l_1,r_1,l_2,r_2-1],[l_1,r_1,l_2,r_2+1]\),那么可以使用二维莫队。
Q:上面是一托啥啊?
A:简单来看就是将一个位置 \(\pm 1\)。
3.2 二维莫队的块长
块长 \(S=n\times q^{\frac{1}{4}}\)。很容易发现这个值可能为 \(0\),此时取 \(1\) 即可。
时间复杂度 \(O(n^2q^{\frac{3}{4}+q\log q})\)。

浙公网安备 33010602011771号