题解: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;
}
posted @ 2026-11-11 15:09  xuyifei0302  阅读(3)  评论(0)    收藏  举报