莫队算法

了解莫队之前先看一下这样一个问题
Q:有一个长为N序列,有M个询问:在区间[L,R]内,出现了多少个不同的数字。(数字范围为0到1000000之间的整数),N ≤ 50000,M ≤ 200000。

对于这个问题不难想到一个简单的暴力

int L=1,R=0;
void add(int x)
{
    cnt[x]++;
    if(cnt[x]==1) sum++;
}

void del(int x)
{
    cnt[x]--;
    if(cnt[x]==0) sum--;
}

for(int i=1;i<=m;++i)
{
    while(R<q[i].rr) add(a[++R]);
    while(R>q[i].rr) del(a[R--]);
    while(L<q[i].ll) del(a[L++]);
    while(L>q[i].ll) add(a[--L]);
    ans[q[i].num]=sum;
}

就是用一个cnt[]数组记录当前每个数字出现了多少次
然后用左L,右R指针不断移动来获得当前区间的cnt[]数组
增加时每当有一个cnt[i]==1就代表新增加了一个数字
删除时每当有一个cnt[i]==0就代表新减少了一个数字

但是既然都说了这是一个暴力,这样当然是过不了
不过这就是莫队算法的其中一个核心了

考虑到这样做会T的原因是左右指针移动次数无法确定
所以我们可以改变对询问的处理顺序,采用离线算法

具体怎么改变询问顺序呢
我们将整个序列按分块思想分成 N 块,每块有 N 个元素
然后将询问排序,具体方法是
询问左区间L所在块为第一关键字
询问右区间R为第二关键字排序

现在所有询问左区间所在块是单调递增的
所以左指针移动次数不超过 M N

由于同一个块内所有询问右指针单调递增
一个块内的询问右指针移动最大为 N ,总共 N 个块
所以右指针移动次数不超过 N N

整体复杂度 O ( ( N + M ) N )

BZOJ1878[SDOI2009]HH的项链【莫队】题解
上述问题就是这题
完整代码在这篇博客里

猜你喜欢

转载自blog.csdn.net/niiick/article/details/80327593