牛客:牛牛的mex(莫队+分块)

题意:

链接:https://ac.nowcoder.com/acm/problem/210690
来源:牛客网

牛牛现在有一个长度为 nnn 的序列 a1,a2,…,ana_1,a_2,ldots,a_na1,a2,,an。现在牛牛有 qqq 次询问,每次想询问区间 [l,r][l,r][l,r] 的 mex 是什么。

一个序列的 mex 定义为最小未出现的自然数。

题解:

直接暴力就能过,但是如果去掉题目中的互不相同性质,暴力就不行,

这里给出一个支持扩展版本的做法,就是莫队加分块。

莫队优化询问,然后从1~num遍历分好的块,遇到有块空的就在这个块里面找答案。

总体时间复杂度是O(n√n)。

#include<bits/stdc++.h>
using namespace std;
const int maxn=1e5+100;
int a[maxn];
int n,m;
struct query {
    int l,r,id;
}q[maxn];
int ans[maxn];
int belong[maxn];
int size;
int bnum;
int cnt[maxn];
int st[maxn];//每个块的起点 
int ed[maxn];//每个块的终点 
int pos[maxn];//每个点属于哪个块 
int mk[maxn];//标记每个块的现有元素数量 
int num[maxn];//每个块的大小 
int now;
int cmp (query a,query b) {
    return (belong[a.l]^belong[b.l])?belong[a.l]<belong[b.l]:((belong[a.l]&1)?a.r<b.r:a.r>b.r);
}
void add (int p) {
    cnt[a[p]]++;
    if (a[p]==0) return;
    if (cnt[a[p]]==1) mk[pos[a[p]]]++;
}
void del (int p) {
    cnt[a[p]]--;
    if (a[p]==0) return;
    if (cnt[a[p]]==0) mk[pos[a[p]]]--;
}
int main () {
    scanf("%d%d",&n,&m);
    int tt=sqrt(n);
    for (int i=1;i<=tt;i++) {
        st[i]=(i-1)*tt+1;
        ed[i]=i*tt;
        num[i]=tt;
    }
    if (ed[tt]<n) {
        tt++;
        st[tt]=ed[tt-1]+1;
        ed[tt]=n;
        num[tt]=n-(tt-1)*(tt-1);
    }
    for (int i=1;i<=tt;i++) {
        for (int j=st[i];j<=ed[i];j++) {
            pos[j]=i;
        }
    }
    size=sqrt(m);
    bnum=m/size+(m%size>0);
    for (int i=1;i<=bnum;i++) {
        for (int j=(i-1)*size+1;j<=i*size;j++)
            belong[j]=i;
    }
    
    for (int i=1;i<=n;i++) scanf("%d",&a[i]);
    for (int i=1;i<=m;i++) {
        int l,r;
        scanf("%d%d",&q[i].l,&q[i].r);
        q[i].id=i;
    }
    sort(q+1,q+m+1,cmp);
    int l=1,r=0;
    for (int i=1;i<=m;i++) {
        int ql=q[i].l;
        int qr=q[i].r;
        while (l<ql) del(l++);
        while (l>ql) add(--l);
        while (r<qr) add(++r);
        while (r>qr) del(r--);
        if (!cnt[0])
            now=0;
        else {
            now=0;
            for (int i=1;i<=tt;i++) {
                if (mk[i]<num[i]) {
                    for (int j=st[i];j<=ed[i];j++) {
                        if (!cnt[j]) {
                            now=j;
                            break;
                        }
                    }
                    break;
                }
            }
              
        }
        ans[q[i].id]=now;
    }
    for (int i=1;i<=m;i++) printf("%d
",ans[i]);
}
原文地址:https://www.cnblogs.com/zhanglichen/p/13580173.html