莫队学习笔记

莫队

莫队算法是由莫涛巨佬提出的离线查询优化算法。

思想

使用莫队算法的前提是我们在知道某区间l,r的结果后,可以在很短的时间内求出l+1,r和l,r+1和l-1,r和l,r-1的结果

但是很多出题人就会卡你这种暴力转移,让你从头跑到尾又从尾跑到头

所以这时候我们就需要通过莫队算法来排序这些询问,让我们可以在尽量短的时间内跑完

实现

如何排序这些离线询问?

例题:[SDOI2009]HH的项链

首先简化题意:给定一个序列,多次询问某区间内有多少不同的数字。

一个简单的想法:因为我们知道了l,r区间中有几个不同的数字,我们每次移动时只需要看看新加入的数字有没有出现过就好了

这样可以做到\(O(1)\)的移动

但是,当相邻两个询问特别远,我们就会耗费大量的时间,于是我们就可以使用莫队排序。

分块

这个分块不是指数据结构而是一种把问题分类的方式

我们把整体分成几个小块以方便后面的排序

最常见的块的大小是\(/sqrt(n)\)

代码:

int bl=sqrt(n);//即block lenth,块长
for(register int i=1;i<=n;++i){
     read(a[i]);
     be[i]=i/bl-1;
}

核心排序

我们上面已经分好块了,那我们接下来怎么办呢?

首先,按照每个询问的左端点所属的块进行排序,然后按照右端点的大小进行排序就可以了

代码:

bool cmp(query a,query b){
    return be[a.l]==be[b.l]?a.r<b.r:a.l<b.l;
}

排序的优化:奇偶排序

首先我们的右端点经过排序之后貌似还是会跳来跳去

所以我们选择在第奇数个块的时候右端点按从小到大排序,第偶数个块的时候按从大到小排序,可以节省一些转移时间

代码:

bool cmp(query a,query b){
    return be[a.l]==be[b.l]?(be[b.l]&1)?a.r<b.r:a.r>b.r:a.l<b.l;
}

完整代码

#include<bits/stdc++.h>
using namespace std;
inline void read(int &x){
    char ch=getchar(); x=0;
    while(ch<'0') ch=getchar();
    while(ch>='0' && ch<='9') x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
}
char buf[201];
inline void out(int x){
    register int cnt=0;
    while(x)buf[++cnt]=(x%10)+48,x/=10;
    while(cnt) putchar(buf[cnt--]);
    putchar('\n');
}
struct query{
    int l,r;
    int id;
}q[1000001];
int be[1000001];
int cnt[1000001];
int ans[1000001];
int a[1000001];
int sum;
bool cmp(query a,query b){
    return be[a.l]==be[b.l]?(be[b.l]&1)?a.r<b.r:a.r>b.r:a.l<b.l;
}
int main(){
    int n,m;
    read(n);
    int bl=sqrt(n);
    for(register int i=1;i<=n;++i){
        read(a[i]);
        be[i]=i/bl-1;
    }
    read(m);
    for(register int i=1;i<=m;++i){
        read(q[i].l),read(q[i].r);
        q[i].id=i;
    }
    sort(q+1,q+1+m,cmp);//千万不要忘记排序
    int l=1,r=0;
    for(register int i=1;i<=m;++i){
        while(r<q[i].r){cnt[a[++r]]++;if(cnt[a[r]]==1)sum++;}//cnt用于记录有没有出现过
        while(r>q[i].r){cnt[a[r]]--;if(cnt[a[r--]]==0)sum--;}
        while(l<q[i].l){cnt[a[l]]--;if(cnt[a[l++]]==0)sum--;}
        while(l>q[i].l){cnt[a[--l]]++;if(cnt[a[l]]==1)sum++;}
        ans[q[i].id]=sum;
    }
    for(register int i=1;i<=m;++i){
        out(ans[i]);
    }
}

后续:更难的应用(题目收录)

咕咕咕

猜你喜欢

转载自www.cnblogs.com/youddjxd/p/11070188.html