浅谈「极小 MEX 区间」
lateworker · · 算法·理论
引入
i
首先,我们需要维护一个
例题:P4137 Rmq Problem / mex
算法介绍
现在来讲如何求出所有极小
和求区间
此外我们还需考虑
最后记得特殊处理
例题:P13780「o.OI R2」愿天堂没有分块
实现
实现还是有些讲究的。
注意到
以防你不知道 zkw 线段树是什么,或者如何在 zkw 线段树上二分,这里提供一篇介绍 zkw 线段树的博客:浅析线段树实现
然后就是完整代码了。
#define ffopen(s) \
ios::sync_with_stdio(0), cin.tie(0), cout.tie(0), cerr.tie(0); \
if (*#s) freopen(#s ".in", "r", stdin); \
if (*#s) freopen(#s ".out", "w", stdout); \
//
#include <bits/stdc++.h>
#define chkmax(x, y) ((x)=max((x),(y)))
#define chkmin(x, y) ((x)=min((x),(y)))
using namespace std;
using intl = long long;
using pii = pair<int, int>;
const int N = 500000, inf = 0x3f3f3f3f;
struct Segt {
int st[N * 3 + 10], tn;
void build(int n) {
for (tn = 1; tn <= n + 1; tn <<= 1);
memset(st, 0, (tn + n + 3) * sizeof(*st));
}
void pushup(int u) { if (u) st[u] = min(st[u << 1], st[u << 1 | 1]); }
void modify(int u, int val) { st[u += tn] = val; do pushup(u >>= 1); while (u); }
int query(int l, int r) {
int res = inf;
for (l += tn, r += tn + 1; l < r; l >>= 1, r >>= 1) {
if (l & 1) chkmin(res, st[l++]);
if (r & 1) chkmin(res, st[--r]);
} return res;
}
int find(int pos) {
int u = 1;
while (u < tn) {
int now = st[u <<= 1];
if (now >= pos) u ^= 1;
} return u - tn;
}
int operator[] (const int& i) { return st[i + tn]; }
} las;
// zkw 线段树板子,重载了 [] 运算,las[i] 等价于 query(i, i)
int n, a[N + 10];
vector<pii> res[N + 10]; // 求出的极小mex区间
int main() {
cin >> n;
for (int i = 1; i <= n; i++) cin >> a[i];
las.build(n);
for (int i = 1; i <= n; i++) {
int p = las[a[i]]; las.modify(a[i], i);
if (a[i]) res[0].emplace_back(i, i);
for (int j = las.query(0, a[i]), mex; j > p; j = las[mex]) {
mex = las.find(j);
res[mex].emplace_back(j, i);
}
}
// 输出所有极小 mex 区间
for (int mex = 0; mex <= n; mex++) {
for (auto [l, r] : res[mex]) {
cout << l << ' ' << r << '\n';
}
cout << '\n';
}
return 0;
}