牛客练习赛68 莫队算法
链接:https://ac.nowcoder.com/acm/contest/7079/A
题目描述
牛牛现在有一个长度为 nnn 的序列 a1,a2,…,ana_1,a_2,\ldots,a_na1,a2,…,an。现在牛牛有 qqq 次询问,每次想询问区间 [l,r][l,r][l,r] 的 mex 是什么。
一个序列的 mex 定义为最小未出现的自然数。
输入描述:
第一行两个整数 n,q 表示序列长度和询问次数。
接下来一行 n 个非负整数,表示序列 a。
接下来 q行,每行两个整数 l,r表示询问的区间。
输出描述:
q行,每行表示询问的答案。
示例1
输入
5 2 4 3 0 1 2 2 4 1 5
输出
2 5
#include <cstdio> #include <cstring> #include <cmath> #include <algorithm> #define isdigit(x) ((x) >= '0' && (x) <= '9') using namespace std; const int maxn=1e5+7; int aa[maxn], cnt[maxn]; int n,m, size, bnum, now, ans[maxn]; struct node { int l, r, id, bl; bool operator< (const node& b)const { if(bl != b.bl) return bl < b.bl; return r < b.r; } }q[maxn + 5]; int read() { int res = 0; char c = getchar(); while(!isdigit(c)) c = getchar(); while(isdigit(c)) res = (res << 1) + (res << 3) + c - 48, c = getchar(); return res; } void printi(int x) { if(x / 10) printi(x / 10); putchar(x % 10 + '0'); } void add(int x){ ++cnt[aa[x]]; while(cnt[now]) ++now; } void del(int x){ --cnt[aa[x]]; if(!cnt[aa[x]]) now=min(now,aa[x]); } int main() { scanf("%d%d", &n,&m); size = sqrt(n); for(int i = 1; i <= n; ++i) aa[i] = read(); //m = read(); for(int i = 1; i <= m; ++i) { q[i].l = read(), q[i].r = read(); q[i].id = i; q[i].bl=q[i].l/size; } sort(q + 1, q + m + 1); int l = 1, r = 0; for(int i = 1; i <= m; ++i) { int ql = q[i].l, qr = q[i].r; while(l < ql) del(l),++l; while(l > ql) --l,add(l); while(r < qr) ++r,add(r); while(r > qr) del(r),--r; ans[q[i].id] = now; } for(int i = 1; i <= m; ++i) printi(ans[i]), putchar('\n'); return 0; }
题解:
莫队优化的暴力,考虑清楚区间增删时对ans的影响就能ac。
下面对l,r移动时的情况进行考虑,
首先如果区间增加时 nex出现了即cnt=1,那么我们就要往后找cnt为0的值更新为nex
区间减少时,nex减少了,那么我们只需要比较当前的nex与删掉的a[x]的大小,取最小的为nex即可。