Mex 相关
搞了一上午这些玩意儿,有点破防。
\(O(\log n)\) 在线求 \(\rm{mex}\)
考虑可持久化权值线段树,每个版本 \(i\) 维护了每个数在 \([1,i]\) 中最后出现的位置。
对于区间 \([l,r]\) 的查询,在可持久化权值线段树的第 \(r\) 个版本上查小于 \(l\) 的第一个数值,这里线段树上二分即可。
代码
#include <bits/stdc++.h>
void Freopen() {
freopen("", "r", stdin);
freopen("", "w", stdout);
}
using namespace std;
const int N = 2e5 + 10, M = 2e5 + 10, inf = 1e9, mod = 998244353;
int n, m;
int a[N];
struct sgt {
int rt[N], tot;
int mi[N * 20], ls[N * 20], rs[N * 20];
void ins( int lk, int & k, int l, int r, int x, int v) {
k = ++ tot;
ls[k] = ls[lk], rs[k] = rs[lk];
if (l == r) {
mi[k] = v;
return ;
}
int mid = (l + r) >> 1;
x <= mid ? ins(ls[lk], ls[k], l, mid, x, v) : ins(rs[lk], rs[k], mid + 1, r, x, v);
mi[k] = min(mi[ls[k]], mi[rs[k]]);
}
int ask( int k, int x, int l, int r) {
if (l == r) return l;
int mid = (l + r) >> 1;
if (mi[ls[k]] < x) return ask(ls[k], x, l, mid);
return ask(rs[k], x, mid + 1, r);
}
} T;
signed main() {
ios :: sync_with_stdio(false);
cin.tie(0), cout.tie(0);
cin >> n >> m;
for ( int i = 1; i <= n; i ++)
cin >> a[i],
T.ins(T.rt[i - 1], T.rt[i], 0, N, a[i], i);
while (m --) {
int l, r; cin >> l >> r;
cout << T.ask(T.rt[r], l, 0, N) << '\n';
}
return 0;
}
求解极短、级长 \(\rm{mex}\) 区间。
对于一个区间 \([l,r]\),若不存在 \([l',r']\subsetneqq [l,r]\) 使得 \({\rm{mex}}(a[l',r'])={\rm{mex}}(a[l,r])\),则 \([l,r]\) 是一个极短 \(\rm mex\) 区间。
对于一个区间 \([l,r]\),若不存在 \([l,r]\subsetneqq [l‘,r’]\) 使得 \({\rm{mex}}(a[l',r'])={\rm{mex}}(a[l,r])\),则 \([l,r]\) 是一个极长 \(\rm mex\) 区间。
首先是结论,一个序列的极短、极长 \(\rm{mex}\) 区间个数都为 \(O(n)\)。
记满足 \({\rm mex}(l,r)=x\) 的区间为 \({\rm mex}_x\) 区间。
考虑如何求出所有的极短 \(\rm mex\) 区间。
- 对每个极短 \({\rm mex}_x\) 区间用
vector< pair< int, int> >进行维护。 - 预处理出极短 \({\rm mex}_0\) 与 \({\rm mex}_1\) 区间。
- 对于每个极短的 \({\rm mex}_{x-1}\) 区间 \([l,r]\),分别求出距离它左端点最近的 \(x-1\) 的位置 \(L\)、距离右端点最近的 \(x-1\) 的位置 \(R\),然后可以构成两个区间 \([l, R]\) 与 \([L,r]\),对这两个区间计算出它们的 \(\rm mex\) 记为 \(x'\),然后丢进对应的
vector中。 - 对于 \({\rm mex}_x\) 的所有区间,把被完全包含的去掉,剩下的就是极短 \({\rm mex}_x\) 区间。
代码
cin >> n;
//M 表示值域
for ( int i = 0; i <= M; i ++) vec[i].push_back(0);
for ( int i = 1; i <= n; i ++)
cin >> a[i],
T.ins(T.rt[i - 1], T.rt[i], 0, N, a[i], i),
vec[a[i]].push_back(i);
for ( int i = 0; i <= M; i ++) vec[i].push_back(n + 1);
for ( int i = 1; i <= n; i ++) {
if (a[i] == 0) Mex[1].push_back({i, i});
else Mex[0].push_back({i, i});
}
for ( int i = 1; i <= M; i ++) {
for ( auto [l, r] : Mex[i - 1]) {
int L = * prev(upper_bound(vec[i - 1].begin(), vec[i - 1].end(), l));
int R = * lower_bound(vec[i - 1].begin(), vec[i - 1].end(), r);
// ask(l,r) 就是 [l,r] 的 mex
if (L != 0) Mex[ask(L, r)].push_back({L, r});
if (R != n + 1) Mex[ask(l, R)].push_back({l, R});
}
sort(Mex[i].begin(), Mex[i].end(), [&]( pair< int, int> a, pair< int, int> b) {
return a.first == b.first ? a.second < b.second : a.first > b.first;
});
vector< pair< int, int> > tmp;
int lst = inf;
for ( auto [l, r] : Mex[i]) {
if (lst > r) tmp.push_back({l, r});
lst = min(lst, r);
}
swap(Mex[i], tmp);
}
求出极短后,求极长就很简单了。
对于一个极短 \({\rm mex}_x\) 区间 \([l,r]\),分别求出距离它左端点最近的 \(x\) 的位置 \(L\)、距离右端点最近的 \(x\) 的位置 \(R\),那么 \([L+1,R-1]\) 就是它的极长区间。
[CEOI 2025] Equal Mex
把 \(v_i\) 减 \(1\) 后就变成了 \(\rm mex\)。
分析一下发现,如果 \(k\) 是一个完美的划分,那么 \(k-1\) 也一定是,显然合并两个 \(\rm mex\) 相同的序列 \(\rm mex\) 依旧相同。
同时,对于一个区间 \([l,r]\),若它的 \(\rm mex\) 为 \(x\),那划分出来的所有区间的 \(\rm mex\) 也要为 \(x\)。
那么问题转化成对一个 \(\rm mex\) 为 \(x\) 的区间,求出一个最大的划分使得每个段的 \(\rm mex\) 都是 \(x\)。
那么就很自然地想到求出极短 \(\rm mex\) 区间。
对于一个 \({\rm mex}=x\) 的询问,考虑所有的极短 \({\rm mex}_x\) 区间。
也就是要在所有的极短 \({\rm mex}_x\) 区间中选出最多的区间使得都在 \([l,r]\) 内,且两两无交。
因为极短区间是没有包含关系的,所以每次贪心地选最左边且无交的区间即可。
这里用倍增优化。
代码
#include <bits/stdc++.h>
void Freopen() {
freopen("", "r", stdin);
freopen("", "w", stdout);
}
using namespace std;
const int N = 6e5 + 10, M = 4e5, inf = 1e9, mod = 998244353;
int n, q;
int a[N];
struct sgt {
int rt[N], tot;
int mi[N * 20], ls[N * 20], rs[N * 20];
void ins( int lk, int & k, int l, int r, int x, int v) {
k = ++ tot;
ls[k] = ls[lk], rs[k] = rs[lk];
if (l == r) {
mi[k] = v;
return ;
}
int mid = (l + r) >> 1;
x <= mid ? ins(ls[lk], ls[k], l, mid, x, v) : ins(rs[lk], rs[k], mid + 1, r, x, v);
mi[k] = min(mi[ls[k]], mi[rs[k]]);
}
int ask( int k, int x, int l, int r) {
if (l == r) return l;
int mid = (l + r) >> 1;
if (mi[ls[k]] < x) return ask(ls[k], x, l, mid);
return ask(rs[k], x, mid + 1, r);
}
} T;
int ask( int l, int r) {
return T.ask(T.rt[r], l, 0, M);
}
struct que {
int l, r, id;
} ;
vector< que> qry[N];
vector< pair< int, int> > Mex[N];
vector< int> vec[N];
int f[22][N];
int ans[N];
vector< int> Ans;
std::vector<int> solve(
int n, std::vector<int>& v,
int q, std::vector<std::pair<int, int>>& queries) {
for ( int i = 0; i <= M; i ++) vec[i].push_back(0);
for ( int i = 1; i <= n; i ++)
a[i] = v[i - 1] - 1,
T.ins(T.rt[i - 1], T.rt[i], 0, M, a[i], i),
vec[a[i]].push_back(i);
for ( int i = 0; i <= M; i ++) vec[i].push_back(n + 1);
for ( int i = 1; i <= q; i ++) {
int l = queries[i - 1].first, r = queries[i - 1].second;
qry[ask(l, r)].push_back({l, r, i});
}
for ( int i = 1; i <= n; i ++) {
if (a[i] == 0) Mex[1].push_back({i, i});
else Mex[0].push_back({i, i});
}
for ( int i = 1; i <= M; i ++) {
for ( auto [l, r] : Mex[i - 1]) {
int L = * prev(upper_bound(vec[i - 1].begin(), vec[i - 1].end(), l));
int R = * lower_bound(vec[i - 1].begin(), vec[i - 1].end(), r);
if (L != 0) Mex[ask(L, r)].push_back({L, r});
if (R != n + 1) Mex[ask(l, R)].push_back({l, R});
}
sort(Mex[i].begin(), Mex[i].end(), [&]( pair< int, int> a, pair< int, int> b) {
return a.first == b.first ? a.second < b.second : a.first > b.first;
});
vector< pair< int, int> > tmp;
int lst = inf;
for ( auto [l, r] : Mex[i]) {
if (lst > r) tmp.push_back({l, r});
lst = min(lst, r);
}
swap(Mex[i], tmp);
reverse(Mex[i].begin(), Mex[i].end());
}
for ( int i = 0; i <= M + 1; i ++) {
int len = (int)Mex[i].size();
int ptr = 1;
for ( int k = 1; k <= len; k ++) {
while (Mex[i][ptr - 1].first <= Mex[i][k - 1].second && ptr <= len) ptr ++;
if (ptr <= len) f[0][k] = ptr;
}
for ( int k = 1; k < 22; k ++)
for ( int j = 1; j <= len; j ++)
f[k][j] = f[k - 1][f[k - 1][j]];
for ( auto [l, r, id] : qry[i]) {
int k = lower_bound(Mex[i].begin(), Mex[i].end(), make_pair(l, 0)) - Mex[i].begin() + 1;
if (Mex[i][k - 1].second > r) {
ans[id] = 0;
continue ;
}
int res = 1;
for ( int j = 21; j >= 0; j --) {
if (f[j][k] && Mex[i][f[j][k] - 1].second <= r) {
k = f[j][k], res += (1 << j);
}
}
ans[id] = res;
}
for ( int k = 0; k < 22; k ++)
for ( int j = 1; j <= len; j ++)
f[k][j] = 0;
}
for ( int i = 1; i <= q; i ++) Ans.push_back(ans[i]);
return Ans;
}

浙公网安备 33010602011771号