题解:AtCoder ARC209D A_A_i

闲话

帅炸了。

这是主播被 \(n=1\) 的 case 卡爆了,望周知。

题意

给定长度为 \(n\) 的序列 \(a\),值域为 \([1,n]\),有一些位置未确定。你需要给这些未确定的位置的确定取值,使得序列 \(b_i=a_{a_i}\) 的字典序最小。多测,\(1\leq T\leq 10^5\)\(1\leq n\leq\sum{n}\leq 5\times 10^5\)

题解

首先特判掉一些 corner case。

\(a_1=-1\lor a_1=1\),我们让所有 \(-1\) 的位置都填 \(1\) 显然最优。

\(a\) 中恰好有 \(1\)\(-1\) 位于 \(pos\) 处,考虑若存在 \(i\) 使得 \(a_i>i\land a_i=pos\),我们就令 \(a_{pos}\gets 1\);否则我们令 \(a_{pos}\) 为最靠前的全局最小值的位置(注意这里求全局最小值时要先令 \(a_{pos}\gets pos\))。这是因为,如果存在 \(i\) 使得 \(a_i>i\land a_i=pos\),那么 \(b_i\) 要比 \(b_{pos}\) 更靠前,所以我们肯定优先把 \(b_i\) 置为 \(1\);否则我们就要让 \(b_{pos}\) 尽可能小,在此基础上,考虑到后面的一些满足 \(a_i=pos\) 的位置,我们还要令 \(a_{pos}\) 尽可能小,于是 \(pos\) 自然就取最靠前的全局最小值的位置了。

接下来考虑 \(a\) 中至少有 \(2\)\(-1\) 的情况。

首先和前面一种 case 类似,若存在 \(i\) 使得 \(a_i>i\land a_{a_i}=-1\),则令 \(a_{a_i}\gets 1\)

然后考虑所有 \(a_i=-1\) 的位置,不妨找出 \(a\) 中第一个 \(1\) 的位置 \(p\)\(\forall a_i=-1,a_i\gets p\)。若不存在 \(a_i=1\),则令 \(p\) 为最后一个 \(a_i=-1\) 的位置,强制令 \(a_p\gets 1\)。这样子除了最后一个位置,每个 \(-1\) 对应的 \(b\) 都能取到 \(1\),比较优秀。

但是可以发现,这样没有考虑到那些 \(a_i\neq -1\land a_i<i\land a_{a_i}=-1\)\(i\) 对应的 \(b_i\) 的取值,换句话说,我们还要尽可能最小化 \(a_i\)。怎样可以进一步最小化 \(a_i\) 呢?可以发现,我们只能考虑提前把某个 \(-1\) 的位置置为 \(1\)。考虑最小的 \(i\) 使得 \(a_i\neq -1\land a_i<i\land a_{a_i}=-1\),那么显然 \(i\) 之前的 \(-1\) 不能动,于是考虑 \(i\) 之后第一个 \(j\) 满足 \(a_j=-1\),我们可以尝试把这个 \(a_j\) 置为 \(1\)。具体来说,我们直接令前文中找到的 \(p\) 和这个 \(j\)\(\min\) 即可。

依照上述讨论实现即可。时间复杂度为 \(\mathcal{O}(\sum n)\)

代码
#include <bits/stdc++.h>

using namespace std;

#define lowbit(x) ((x) & -(x))
typedef long long ll;
typedef unsigned long long ull;
typedef long double ld;
typedef pair<int, int> pii;
const int N = 5e5 + 5;

template<typename T> inline void chk_min(T &x, T y) { x = min(x, y); }
template<typename T> inline void chk_max(T &x, T y) { x = max(x, y); }

int T, n, cnt, a[N];

int main() {
	ios::sync_with_stdio(0), cin.tie(0);
	cin >> T;
	while (T--) {
		cin >> n, cnt = 0;
		for (int i = 1; i <= n; ++i) cin >> a[i], cnt += a[i] == -1;
		if (cnt == 1) {
			int pos = 0, mnp = 0; bool flag = 0;
			for (int i = n; i; --i) {
				if (a[i] == -1) pos = i;
				else {
					if (a[i] == pos) { flag = 1; break; }
					if (!mnp || a[i] <= a[mnp]) mnp = i;
				}
			}
			if (!mnp) mnp = pos;
			if (pos < a[mnp] || (pos == a[mnp] && pos < mnp)) mnp = pos;
			a[pos] = flag ? 1 : mnp;
		} else if (a[1] == -1 || a[1] == 1) {
			for (int i = 1; i <= n; ++i) if (a[i] == -1) a[i] = 1;
		} else if (cnt) {
			for (int i = 1; i <= n; ++i) if (a[i] > i && a[a[i]] == -1) a[a[i]] = 1;
			int pos = 1;
			for (int i = 1; i <= n; ++i)
				if (a[i] == 1) { pos = i; break; }
				else if (a[i] == -1) pos = i;
			for (int i = 1; i <= n; ++i) if (~a[i] && a[i] < i && a[a[i]] == -1) {
				int p = i + 1;
				while (p <= n && ~a[p]) ++p;
				chk_min(pos, p); break;
			}
			for (int i = 1; i <= n; ++i) if (a[i] == -1) a[i] = pos;
			a[pos] = 1;
		}
		for (int i = 1; i <= n; ++i) cout << a[a[i]] << " \n"[i == n];
	}
	return 0;
}
posted @ 2025-11-14 21:15  P2441M  阅读(1)  评论(0)    收藏  举报