莫队

洛谷讲解

莫队基本介绍

莫队算法分为两类,一是莫队维护区间答案,二是维护区间内的数据结构。

特殊的也有树上莫队,带修改莫队、二维莫队等等。这篇文章主要介绍的是普通莫队算法


 我们考虑一个问题,给一个序列,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的询问题解

 

posted @ 2022-02-09 19:47  little_sheep_xiaoen  阅读(162)  评论(0编辑  收藏  举报