题解:P13693 [CEOI 2025] Equal Mex
我们发现整个序列中极小 MEX 区间的总数仅为 \(\mathcal{O}(n)\) 个。我们枚举区间的左端点位置 \(l\),令 \(t = v_l\)。设 \(nxt_x\) 为从位置 \(l\) 开始,数字 \(x\) 的第一次出现位置;并设 \(T\) 为数字 \(t\) 在位置 \(l\) 之后的下一次出现位置。定义当前游标 \(cur = \max\left(l, \max_{1 \le x < t} nxt_x\right)\)。若 \(cur < T\),我们可以从小到大依次考察值 \(x = t+1, t+2, \ldots\)。每当 \(nxt_x > cur\) 成立时,我们就得到了一个全新的极小 MEX 区间 \([l, cur]\),此时它的 MEX 值为 \(x\)。找到后,我们将 cur 更新为 \(nxt_x\)。一旦发现 \(nxt_x \ge T\),说明该区间已经包含了下一个 \(t\),在生成当前区间后即可停止向右扩展。特别地,对于 \(t > 1\),还要额外加入一个单元素区间 \([l, l]\),其 MEX 为 \(1\)。这些候选区间可以使用一棵维护序列最大值的线段树来高效生成,每次可以在 \(\mathcal{O}(\log V)\) 时间内在树上二分查找到第一个值大于 cur 的位置。
生成所有极小 MEX 区间后,我们将它们按照 MEX 的值进行分组。对于某一个 MEX 组别 \(x\),将该组内的所有区间按左端点从小到大排序为 \(I_1, I_2, \ldots, I_s\)。由于极小区间的性质,在同一组内,区间的左端点和右端点必定严格单调递增。对于每个区间 \(I_i = [L_i, R_i]\),我们定义它的后继为该组内第一个满足 \(L_j > R_i\) 的区间 \(I_j\),并将这个后继 \(I_j\) 设为 \(I_i\) 的父节点。通过这种方式,我们建立了一棵(或多棵)森林。在这棵森林中,每一条向根方向的路径,恰好对应了一个由不相交极小 MEX 区间组成的贪心跳转序列。
接下来,设 \(u\) 是某个询问在目标 MEX 下匹配到的第一个极小区间,且该询问的右端点为 \(R\)。问题实质上转化为在森林中求 \(u\) 的祖先(含自身)里,区间右端点至多为 \(R\) 的节点个数。为了避免在线倍增跳转,我们对整棵森林求出欧拉序(DFS 序),从而把子树操作转化为连续的一维区间操作。我们将所有询问按右端点 \(R\) 从小到大排序进行离线处理。一旦扫描到某个树节点 \(x\) 的区间右端点满足 \(R_x \le R\),该节点即被激活,我们就在它的整个子树对应的 DFS 序区间上加 \(1\)(使用树状数组支持区间加、单点查)。此时,对于查询起点 \(u\),只需在树状数组的 tin[u](DFS 序进入时间戳)处进行单点查询,得到的值就是 \(u\) 已经被激活的祖先数,也就是所求的答案。这种转化的精妙之处在于,我们不再显式地执行每一次贪心跳转,而是利用子树与祖先的对应关系,通过离线扫描同时统计出祖先路径上所有符合条件的节点。
时间复杂度 \(\mathcal{O}((n+q)\log(n+V))\),空间复杂度 \(\mathcal{O}(n+q+V)\)。
#include <bits/stdc++.h>
using namespace std;
namespace segtree {
int tree[4000005];
void buildtree(int p, int l, int r, int* a) {
if (l == r) {
tree[p] = a[l];
return;
}
int mid = (l + r) >> 1;
buildtree(p * 2, l, mid, a);
buildtree(p * 2 + 1, mid + 1, r, a);
tree[p] = max(tree[p * 2], tree[p * 2 + 1]);
}
void change(int p, int l, int r, int x, int d) {
if (l == r) {
tree[p] = d;
return;
}
int mid = (l + r) >> 1;
if (x <= mid) {
change(p * 2, l, mid, x, d);
} else {
change(p * 2 + 1, mid + 1, r, x, d);
}
tree[p] = max(tree[p * 2], tree[p * 2 + 1]);
}
int get(int p, int l, int r, int ll, int rr) {
if (ll <= l && r <= rr) {
return tree[p];
}
int mid = (l + r) >> 1, maxn = 0;
if (ll <= mid) {
maxn = max(maxn, get(p * 2, l, mid, ll, rr));
}
if (rr > mid) {
maxn = max(maxn, get(p * 2 + 1, mid + 1, r, ll, rr));
}
return maxn;
}
int find(int p, int l, int r, int x, int k) {
if (r < x || tree[p] <= k) {
return -1;
}
if (l == r) {
return l;
}
int mid = (l + r) >> 1, tmp = find(p * 2, l, mid, x, k);
if (tmp != -1) {
return tmp;
}
return find(p * 2 + 1, mid + 1, r, x, k);
}
}
namespace maxbit {
int tree[1000005], lim;
void init(int x) {
lim = x;
fill(tree, tree + 1 + x, 0);
}
int lowbit(int x) {
return x & (-x);
}
void change(int x, int y) {
for (; x <= lim; x += lowbit(x)) {
tree[x] = max(tree[x], y);
}
}
int get(int x) {
int maxn = 0;
for (; x; x -= lowbit(x)) {
maxn = max(maxn, tree[x]);
}
return maxn;
}
}
namespace bit {
int tree[2000005], lim;
void init(int x) {
lim = x;
fill(tree, tree + 1 + x, 0);
}
int lowbit(int x) {
return x & (-x);
}
void change(int x, int y) {
for (; x <= lim; x += lowbit(x)) {
tree[x] += y;
}
}
int get(int x) {
int res = 0;
for (; x; x-= lowbit(x)) {
res += tree[x];
}
return res;
}
}
int nxt[1000005], L[2000005], R[2000005], mex[2000005], mex1[2000005], id[2000005], sum[1000005], cnt[1000005], history[1000005], fir[1000005], last[1000005], queryl[1000005], queryr[1000005], ordQ[1000005], ans[1000005], stNode[1000005], fa[2000005], he[2000005], nxx[2000005], tin[2000005], tout[2000005], sta[2000005], edge[2000005];
vector<int> solve(int n, vector<int>& v, int q, vector<pair<int, int>>& queries) {
int maxn = n + 2;
for (auto &i : v) {
if (i > n + 2) {
i = n + 2;
}
maxn = max(maxn, i + 2);
}
fill(fir, fir + 1 + maxn, n + 1);
fill(last, last + 1 + maxn, n + 1);
fill(cnt, cnt + 1 + maxn, 0);
for (int i = n - 1; i >= 0; i --) {
nxt[i] = last[v[i]];
fir[v[i]] = i + 1;
last[v[i]] = i + 1;
}
segtree::buildtree(1, 1, maxn, fir);
int pos = 0;
for (int i = 0; i < n; i ++) {
int l = i + 1, t = v[i], t1 = nxt[i], cur = l;
if (t > 1) {
pos ++;
L[pos] = l;
R[pos] = l;
mex[pos] = 1;
cnt[1] ++;
cur = max(cur, segtree::get(1, 1, maxn, 1, t - 1));
}
if (cur < t1) {
int x = segtree::find(1, 1, maxn, t + 1, cur);
while (x != -1) {
int now = segtree::get(1, 1, maxn, x, x);
pos ++;
L[pos] = l;
R[pos] = cur;
mex[pos] = x;
cnt[x] ++;
cur = now;
if (cur >= t1) {
break;
}
x = segtree::find(1, 1, maxn, x + 1, cur);
}
}
segtree::change(1, 1, maxn, t, t1);
}
sum[1] = 1;
for (int i = 2; i <= maxn; i ++) {
sum[i] = sum[i - 1] + cnt[i - 1];
}
copy(sum, sum + 1 + maxn, history);
for (int i = 1; i <= pos; i ++) {
mex1[history[mex[i]] ++] = i;
}
for (int i = 1; i <= q; i ++) {
queryl[i] = queries[i - 1].first;
queryr[i] = queries[i - 1].second;
ordQ[i] = i;
}
sort(ordQ + 1, ordQ + q + 1, [](int x, int y){ return queryl[x] > queryl[y]; });
maxbit::init(n);
for (int i = 1, p = pos; i <= q; i ++) {
int now = ordQ[i];
while (p >= 1 && L[p] >= queryl[now]) {
maxbit::change(R[p], mex[p]);
p --;
}
ans[now] = maxbit::get(queryr[now]);
}
for (int i = 1; i <= q; i ++) {
int maxn = ans[i], st = sum[maxn], ed = sum[maxn + 1] - 1, l = st, r = ed, res = -1, mid = (l + r) >> 1;
while (l <= r) {
if (L[mex1[mid]] >= queryl[i]) {
res = mex1[mid];
r = mid - 1;
mid = (l + r) >> 1;
} else {
l = mid + 1;
mid = (l + r) >> 1;
}
}
stNode[i] = res;
}
fill(fa + 1, fa + pos + 1, 0);
fill(he + 1, he + pos + 1, 0);
for (int kk = 1; kk <= maxn; kk ++) {
for (int i = sum[kk], j = sum[kk], ed = sum[kk + 1] - 1; i <= ed; i ++) {
j = max(j, i + 1);
while (j <= ed && L[mex1[j]] <= R[mex1[i]]) {
j ++;
}
if (j <= ed) {
int u = mex1[i], p = mex1[j];
fa[u] = p;
nxx[u] = he[p];
he[p] = u;
}
}
}
int timer = 0, top = 0;
fill(tin + 1, tin + pos + 1, 0);
for (int i = 1; i <= pos; i ++) {
if (!fa[i]) {
sta[++top] = i;
edge[i] = he[i];
tin[i] = ++timer;
while (top) {
int u = sta[top], &e = edge[u];
if (e) {
int c = e;
e = nxx[e];
sta[++top] = c;
edge[c] = he[c];
tin[c] = ++timer;
} else {
tout[u] = timer;
top --;
}
}
}
}
for (int i = 1; i <= pos; i ++) {
id[i] = i;
}
sort(id + 1, id + pos + 1, [](int x, int y){ return R[x] < R[y]; });
sort(ordQ + 1, ordQ + q + 1, [](int x, int y){ return queryr[x] < queryr[y]; });
bit::init(timer);
vector<int> ans(q, 0);
for (int i = 1, p = 1; i <= q; i ++) {
int qid = ordQ[i];
while (p <= pos && R[id[p]] <= queryr[qid]) {
int u = id[p ++];
bit::change(tin[u], 1);
bit::change(tout[u] + 1, -1);
}
ans[qid - 1] = stNode[qid] != -1 ? bit::get(tin[stNode[qid]]) : 0;
}
return ans;
}
本文来自博客园,作者:xuyifei0302,转载请注明原文链接:https://www.cnblogs.com/xuyifei0302/p/23629045

浙公网安备 33010602011771号