莫队
莫队基本介绍
莫队算法分为两类,一是莫队维护区间答案,二是维护区间内的数据结构。
特殊的也有树上莫队,带修改莫队、二维莫队等等。这篇文章主要介绍的是普通莫队算法
我们考虑一个问题,给一个序列,m次询问,每次询问你区间 [l,r] 有多少种不同的颜色。
其中n,m $\leq$ 100000
尽管可以用主席树做,但是我们假装不行
先考虑暴力,对每次询问遍历一遍 [l,r] ,这样是 $O(nm)$ 的,
换种方式暴力,定义$ql$和$qr$,表示区间 [ql,qr] 内有多少颜色,再定义$cnt$数组, $cnt_i$ 表示第i种颜色在区间 $[ql,qr]$ 出现了多少次。
我们一个一个询问处理,对于询问 [l,r] ,我们挪动xl到l,xr到r
因为这个是莫队算法的基础,所以模拟一下这个过程:
我们的初始在这个状态,假设蓝色标号为1,红色为2,绿色为3,$cnt_i$表示该颜色出现次数
那么 $cnt_1=3 , cnt_2=3 , cnt_3=1$ ,我们把qr向右挪一下
这样多了一个绿色, $cnt_3$ 加1
qr继续右挪一下
这样就又多了一个红色, $cnt_2$ 加1
此时我们发现,我们的右指针qr已经和询问的右端点$r$重合了,接下来挪左指针
这样范围里的就少了一个蓝色, $cnt_1$ 减去1
现在我们得出了答案为3, $cnt_1=2 , cnt_2=4 , cnt_3=2$
提供这部分的代码:
void add( int x ){ if( cnt[x] == 0 ) ans++;//如果之前没有这个颜色,总颜色数量增加 cnt[x]++; } void del( int x ){ cnt[x]--; if( cnt[x] == 0 ) ans--;//如果减了之后就没了该颜色,ans-- }
我们发现,每次挪动都都是 O(1) 的,每次询问我们最多要挪动n次,所以时间复杂度还是 O(nm) 的暴力时间。
我们有没有办法来加速呢?
由于这种暴力的耗时就耗在挪动次数上,我们要让他挪动的次数尽量少
办法就是,我们先将询问储存下来,再按照某种方法排序,让他减少挪动的次数,这样会变快一些
这种方法是强行离线,然后排序,所以这样的普通莫队不能资瓷修改
那么怎么排序呢?
我们的排序是要使左右端点挪动的次数尽量少,所以这里就有一种排序方法:
将序列分成$\sqrt{n}$个长度为$\sqrt{n}$的块,
若右端点在同一个块内,则按左端点排序(以右端点所在块为第一关键字,左端点为第二关键字)
注意,莫队的挪动操作要尽量快,若不是 O(1) 的,复杂度会原地爆炸
对于n与m同阶,一般可以设块长度为$\sqrt{n}$
题目
刚刚那道数颜色其实是 [SDOI2009]HH的项链,然而现在裸的莫队过不去了。
然而莫队加个吸口氧卡卡常还是能过的,不开O2会TLE
还有一个裸的莫队板子题(P2709 小B的询问)题解