牛客:牛牛的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\n",ans[i]); }