莫队

0. 离线与在线

在线算法一般是 预处理 + 输出。离线算法一般是 输入 + 一起处理。

一个典型的例子是求 LCA,有两种方法:倍增、Tarjan。

倍增是预处理所有节点的 2k2^k2k 祖先,然后每次暴力跳。

Tarjan 是输入完所有数据,再全部放在树上处理。

1. 普通莫队

1.1 莫队主体思想

如果 [l,r][l,r][l,r] 能够 O(1)O(1)O(1) 扩展到 [l−1,r],[l+1,r],[l,r−1],[l,r+1][l-1,r],[l+1,r],[l,r-1],[l,r+1][l1,r],[l+1,r],[l,r1],[l,r+1]且不强制在线,那么可以使用分块。

莫队 = 分块 + 暴力 = 优雅的分块 = 优雅的优雅暴力。

1.2 分块部分与查询顺序

为了保证时间复杂度,需要对原序列进行分块。

定义一次询问的块在 ⌊lkc⌋\left\lfloor\dfrac{l}{kc}\right\rfloorkcl

我们对询问的定义是这样的:

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;
	}
};

如果两个块不在一块,考虑按左端点排序。这样可以保证 lll 大体上从左往右递增。

如果两个块在一块,使用奇偶性排序。

详见 oi.wiki

1.3 处理答案

处理答案的常用方法是从已知区间 [l,r][l,r][l,r] 扩展到 [L,R][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 部分较为灵活:如果 xxx 位置出现/失去,会对答案造成怎样的影响。

1.4 时间复杂度分析

左端点在每个块时间复杂度为 O(n)O(n)O(n)(左端点移动具有单调性),共 O(n)O(\sqrt n)O(n) 块,时间复杂度 O(nn)O(n\sqrt n)O(nn)

右端点在每个块时间复杂度为 O(n)O(n)O(n),共 O(n)O(\sqrt n)O(n) 个块,时间复杂度 O(nn)O(n\sqrt n)O(nn)

所以莫队时间复杂度 O(nn)O(n\sqrt n)O(nn)

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 的袜子

经典莫队题。

我们知道一种颜色的袜子的贡献为 12cnt(cnt−1)\dfrac{1}{2}cnt(cnt-1)21cnt(cnt1),所以 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][l,r,time]

如果 [l,r,time][l,r,time][l,r,time] 可以 O(1)O(1)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][l-1,r,time],[l+1,r,time],[l,r-1,time][l,r+1,time],[l,r,time-1],[l,r,time+1][l1,r,time],[l+1,r,time],[l,r1,time][l,r+1,time],[l,r,time1],[l,r,time+1],那么可以使用带修莫队。

带修莫队的时间复杂度一般为 O(n2/3m2/3t1/3)O(n^{2/3}m^{2/3}t^{1/3})O(n2/3m2/3t1/3) 也就是常说的 O(n5/3)O(n^{5/3})O(n5/3)

2.2 带修莫队

第一关键字是左端点所在块,第二关键字是右端点所在块,第三关键字是时间。

块长设置 O(n2/3)O(n^{2/3})O(n2/3)

2.3 【模板】树状数组

2.4 「国家集训队」数颜色/维护队列

3. 二维莫队

3.1 二维莫队的原理

二维莫队的思路同带修莫队。

考虑维护 [l1,r1,l2,r2][l_1,r_1,l_2,r_2][l1,r1,l2,r2],如果能 O(1)O(1)O(1) 转移 [l1−1,r1,l2,r2],[l1+1,r1,l2,r2],[l1,r1−1,l2,r2],[l1,r1+1,l2,r2],[l1,r1,l2−1,r2],[l1,r1,l2+1,r2],[l1,r1,l2,r2−1],[l1,r1,l2,r2+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][l11,r1,l2,r2],[l1+1,r1,l2,r2],[l1,r11,l2,r2],[l1,r1+1,l2,r2],[l1,r1,l21,r2],[l1,r1,l2+1,r2],[l1,r1,l2,r21],[l1,r1,l2,r2+1],那么可以使用二维莫队。

Q:上面是一托啥啊?

A:简单来看就是将一个位置 ±1\pm 1±1

3.2 二维莫队的块长

块长 S=n×q14S=n\times q^{\frac{1}{4}}S=n×q41。很容易发现这个值可能为 000,此时取 111 即可。

时间复杂度 O(n2q34+qlog⁡q)O(n^2q^{\frac{3}{4}+q\log q})O(n2q43+qlogq)

4. 树上莫队

5. 回滚莫队

posted @ 2024-11-23 09:17  sLMxf  阅读(55)  评论(1)    收藏  举报  来源