洛谷 P4137 Rmq Problem mex 莫队 + 值域分块
Rmq Problem / mex
题目描述
有一个长度为 \(n\) 的数组 \(\{a_1,a_2,\ldots,a_n\}\)。
\(m\) 次询问,每次询问一个区间内最小没有出现过的自然数。
输入格式
第一行,两个正整数 \(n,m\)。
第二行,\(n\) 个非负整数 \(a_1, a_2, \ldots , a_n\)。
接下来 \(m\) 行,每行两个正整数 \(l,r\),表示一次询问。
输出格式
输出 \(m\) 行,每行一个数,依次表示每个询问的答案。
样例 #1
样例输入 #1
5 5
2 1 0 2 1
3 3
2 3
2 4
1 2
3 5
样例输出 #1
1
2
3
0
3
提示
对于 \(30\%\) 的数据:\(1\leq n,m\leq 1000\)。
对于 \(100\%\) 的数据:\(1\leq n,m\leq 2\times {10}^5\),\(1\leq l\leq r\leq n\),\(0\leq a_i\leq 2\times 10^5\)。
SOLUTION
对值域进行分块,当每个块里面存在的数字为块长的时候,\(mex\) 一定不在这个区间内,因此查询答案的时候可以暴力枚举每个块,每次查询复杂度为 \(O(\sqrt n)\) ,\(add\) 和 \(del\) 操作的复杂度均为 \(O(1)\),总体复杂度为 \(O(n \sqrt n)\)。
#include <bits/stdc++.h>
using namespace std;
using LL = long long;
// #define int __int128
namespace io {
constexpr int BUFFER_SIZE = 1 << 16;
char buffer[BUFFER_SIZE], *head, *tail;
char get_char() {
if (head == tail) {
int l = (int) fread(buffer, 1, BUFFER_SIZE, stdin);
tail = (head = buffer) + l;
}
return *head++;
}
int read() {
int x = 0, f = 1;
char c = get_char();
for (; !isdigit(c); c = get_char())
if (c == '-') f = -1;
for (; isdigit(c); c = get_char()) x = x * 10 + c - '0';
return x * f;
}
void print(int x) {
static int sta[1997];
int top = 0;
do {
sta[top++] = x % 10, x /= 10;
} while (x);
while (top) putchar(sta[--top] + 48); // 48 是 '0'
}
void println(int x) { print(x), putchar('\n'); }
} // namespace io
using namespace io;
const int N = 2e5 + 10, M = 510;
int n, m, block;
int belong[N], cnt[N], a[N], ans[N], tot[N], bl[N], br[N];
struct node {
int l, r, id;
bool operator < (const node &T) const {
return belong[l] ^ belong[T.l] ? belong[l] < belong[T.l] : ((belong[l] & 1) ? r < T.r : r > T.r);
}
} modui[N];
void add(int x) {
if (!cnt[x]) tot[belong[x]] ++;
cnt[x] ++;
}
void del(int x) {
if (cnt[x] == 1) tot[belong[x]] --;
cnt[x] --;
}
int query() {
int now = 0;
while (tot[now] == br[now] - bl[now] + 1) ++ now;
for (int i = bl[now]; i <= br[now]; i ++ ) {
if (!cnt[i]) return i;
}
return -1;
}
int main() {
n = read(); m = read(); block = sqrt(n); // 0, ...
belong[0] = 0;
for(int i = 1; i <= n; i ++ ) { a[i] = min(read(), n + 1); belong[i] = i / block; }
belong[n + 1] = (n + 1) / block; belong[n + 2] = (n + 2) / block;
for(int i = 1; i <= m; i ++ ) {
modui[i].l = read(), modui[i].r = read(); modui[i].id = i;
}
for (int i = 0; i <= belong[n + 1]; i ++ ) {
bl[i] = i * block;
br[i] = bl[i] + block - 1;
}
sort(modui + 1, modui + 1 + m);
int lal = modui[1].l, lar = modui[1].l - 1;
for(int i = 1; i <= m; i ++ ) {
int l = modui[i].l, r = modui[i].r;
while(lal > l) lal --, add(a[lal]);
while(lar < r) lar ++, add(a[lar]);
while(lal < l) del(a[lal]), lal ++;
while(lar > r) del(a[lar]), lar --;
ans[modui[i].id] = query();
}
for(int i = 1; i <= m; i ++ ) {
println(ans[i]);
}
return 0;
}