tags
构造
贪心
思维
中文题面
题目描述
给出一个长度为 n 的序列 A,你需要找到一个长度为 n 的序列 B,满足 Ai=mex({B1,B2,…,Bi})。
其中 mex 函数的结果是最小的未出现在集合中的非负整数。
输入格式
第一行一个整数 n。
之后一行 n 个整数,表示给出的序列 A。
保证 1≤n≤10^5,0≤ai≤i,对于 1≤i<n,ai≤ai+1。
输出格式
如果序列 B 不存在,输出 -1。
否则输出一行 n 个整数表示你找到的 B,若有多个满足条件的序列,输出任意一个。
输入输出样例
输入 #1
3
1 2 3
输出 #1
0 1 2
输入 #2
4
0 0 0 2
输出 #2
1 3 4 0
输入 #3
3
1 1 3
输出 #3
0 2 1
思路
经过手写举例和对题目样例的分析后发现,当a[i] != a[i - 1]
即数字发生变化时构造b[i] = a[i - 1]
是最好的选择,因为当a[i]
仍在场时我们保证a[i]
为未加入数集中的最小非负整数(mex),而当a[i]
离场时我们必须改变这个mex为a[i + 1]
,而a[i + 1] > a[i]
,则把a[i]
加入已有数集是必要的
现在只剩下a[i] == a[i - 1]
这一种情况需要考虑,我们不妨用一种贪心策略,试想在数字变化(假设变换为c
)时,我们只能操作一步(构造一次b[i]
)使得mex变化,则在变化前应当尽可能使a[i]
成为c
值之下唯一未被加入数集的(如果c
值之下除a[i]
的所有数都已被加入数集,但仍为变化数字,此时应继续向数集中依次加入除未遍历过但在a数组中的值
之外的值)
特别地,题目要求0 <= b[i] <= 1e6
,这就意味着我们不能无限贪心,它有一个跳出的时间点
当贪心的数字超过1e6时说明a[i] == a[i - 1]
情况下已不需要往数集中加入新数字了,这时候我们只需要在再次遇到a[i] == a[i - 1]
时使用某数来占位即可了。
那如何确定这个数呢?
我们考虑找一个不在a数组中的数,这样就不会受到限制,只需遍历最多一整遍0到1e6,找到其中未在a中出现过的数即为我们需要的占位数
突然发现a数组最大为1e5而b数组最大为1e6,则1e5到1e6中间任何一个数都必然不会在a中出现,可以作为占位数
代码
// Edit by Mr_Way
#include <bits/stdc++.h>
using namespace std;
// Edit by Mr_Way
int f = 1;
int a[1000005];
int ban[1000005];
int main() {
int n, tmp = 1e5+1;
int now = 0;
cin >> n;
for (register int i = 1; i <= n; i++) {
cin >> a[i];
ban[a[i]] = 1;
if (a[i] > i) f = 0;
}
if (!f) {
cout << -1;
return 0;
}
// for (register int i = 0; i <= 1e6; i++) {
// if (!ban[i]) {
// tmp = i;
// break;
// }
// }
for (register int i = 1; i <= n; i++) {
if (i > 1 && a[i] != a[i - 1]) cout << a[i - 1] << ' ';
else {
while (true) {
if (!ban[now]) {
if (now > 1e6) cout << tmp << ' ';
else cout << now++ << ' ';
break;
} else now++;
}
}
}
return 0;
}
// Edit by Mr_Way