CF1364-C. Ehab and Prefix MEXs
题意
给出一个长度为n的数组a,然你找出一个长度为n的数组b,使得:
对于每一个 \(i(1<=i<=n)\) 有\(MEX(b_1,b_2,...,b_i)=a_i\).
\(MEX\)的定义如下:\(MEX(b_1,b_2,...,b_i)\)为最小的不在\(\{b_1,b_2,...,b_i\}\)集合中的非负整数。
题目保证数组\(a\)是非降序的。
思路
首先考虑到,第一个数字,如果\(a_1\)为\(1\)那么\(b_1\)一定为\(0\);如果\(a_1\)为\(0\)那么\(b_1\)理论上来说可以是任何数字,之所以说是理论上,是因为这个数字可能对后面会有影响;如果\(a_1\)大于\(1\)那么就不存在这样的数组\(b\)了。
其次考虑到,在\(a_i=a_{i+1}\)的情况下(这里假设\(a_i=x\)),数组中不可能出现数字\(x\),那出现什么情况数组\(b\)才可以出现数字\(x\)呢?只有当\(a_{i-1}=x\not=a_{i}\)的时候(这里假设\(i=p\)),才能出现数字\(x\),并且这个数字\(x\)必须在\(b_{p}\)这个位置出现。原因很明显,数字\(x\)之所以能在\(p\)之前得到,一定是因为\(x\)是\(p\)之前最小的,不再集合中的非负整数,如果\(b_p\not=x\)那么当\(i=p\)的时候一定还有\(MEX(b_1,b_2,..,b_p)=x\),不符合要求,所以\(b_p=x\).
现在对于数组\(b\),对于每个满足\(a_i\not=a_{i-1}\)的位置都已经确定了数字,对于第一个位置可能能确定数字。之后从头到尾对数组\(a\)扫描一遍,用一个计数器记录不能确定数字的位置个数,当扫描到一个位置有\(a_i\not=a_{i-1}\)的时候,比较计数器的数量是否大于等于\(a_i-a_{i-1}-1\)。原因是要想在\(i\)这个位置有\(MEX(b_1,b_2,...,b_i)=a_i\)必须要把\(a_{i-1}\)到\(a_i-1\)的数字‘填’平,那么你要天平就至少需要\(a_i-a_{i-1}-1\)这么多“空闲”的位置去补充,之所以减去1是因为\(a_{i-1}\)已经在\(b_i\)这个位置补充好了。如果大于那么就把这之间空闲的数字填上去,如果还有空余可以留着给后面的数字补充。
如果最后全部补充完毕还有空闲的数字,那么在不改变\(MEX\)值的情况下可以把这些数字改改成任意值,个人的处理方法是把他们全部变为无穷大。
AC代码
#include <cstdio>
#include <cstring>
#include <iostream>
const int Maxn = 100005;
const int INF = 100001;
int a[Maxn], b[Maxn];
int p[Maxn], s = 0, t = 0;
void solve() {
int n;
scanf("%d", &n);
for (int i = 1; i <= n; i++) {
scanf("%d", a + i);
}
if (a[1] != 1) {
p[t++] = 1;
} else {
b[1] = 0;
}
bool flag = true;
for (int i = 2; i <= n; i++) {
if (a[i] != a[i - 1]) {
b[i] = a[i - 1];
if (t - s >= a[i] - a[i - 1] - 1) {
for (int j = a[i - 1] + 1; j <= a[i] - 1; j++) {
b[p[s++]] = j;
}
} else {
flag = false;
break;
}
} else {
p[t++] = i;
}
}
while (s != t) {
b[p[s]] = INF;
s++;
}
if (flag) {
for (int i = 1; i <= n; i++) {
printf("%d%c", b[i], " \n"[i == n]);
}
} else {
printf("-1\n");
}
}
int main() {
solve();
return 0;
}