题解 CF2207E1 【N-MEX (Constructive Version)】
方便起见,令
考虑
- (1)
0 \leq \text{need}_i \leq i, \forall 1 \leq i \leq n 。
另外,注意到在从
- (2)
a_i \geq a_{i + 1}, \forall 1 \leq i < n 。
当 (1)(2) 皆满足,考虑一步一步构造:
- 第一类填数:若
a_i = a_{i - 1} ,贪心地,令b_i 为\{0, \cdots, n\} \backslash a 中< a_i 且没有被用过的最大数。 - 第二类填数:若
a_i < a_{i - 1} ,我们需要填入一个\geq a_{i - 1} 或被用过的数。 - 考虑取
b_i = 10^9 。由于a 单调不增,这个数必然不会对a_{i + 1 \sim n} 的值产生任何影响。
那么,只要满足 (1)(2),这一算法就一定能构造出一组解吗?事实上是可以的:
- 设
a_{0 \sim n} 可以被划分为k 个a 值相同的连续段,其中第i 段的长度为l_i 。 - 则我们一共进行了
\displaystyle\sum_{i = 1}^k (l_i - 1) = (n + 1) - k 次第一类填数。 - 注意到
\{0, \cdots, n\} \backslash a 中也恰好有(n + 1) - k 个数,因此只需说明:讨论a_i 时,取出\{0, \cdots, n\} \backslash a 中没有被用过的最大数,其一定< a_i 。 - 而当我们进入第
i 段,首项下标为p = \displaystyle\sum_{j = 1}^{i - 1} l_j ,前面有i - 1 个a 值和\displaystyle\sum_{j = 1}^{i - 1} (l_j - 1) 个第一类填数指定的b 值,合计恰p 个数;再加上a_p 自身,可知下一次第一类填数指定的b 值不应超过n - p - 1 ,而 (1) 指出n - p \leq a_p ,故n - p - 1 < a_p ,遂合法。
综上,时间复杂度为
代码:
#include <stdio.h>
int a[200001], b[200001];
bool mark[2000001];
void solve(){
int n;
scanf("%d", &n);
for (int i = 1; i <= n; i++){
scanf("%d", &a[i]);
}
for (int i = 1; i <= n; i++){
int need = (a[i] + 1) - (n - i + 1);
if (need < 0 || need > i){
printf("NO\n");
return;
}
}
a[0] = n;
for (int i = 1; i <= n; i++){
if (a[i] > a[i - 1]){
printf("NO\n");
return;
}
}
for (int i = 0; i <= n; i++){
mark[i] = true;
}
for (int i = 0; i <= n; i++){
mark[a[i]] = false;
}
for (int i = 1, j = n; i <= n; i++){
if (a[i] == a[i - 1]){
while (j >= 0 && !mark[j]) j--;
b[i] = j--;
} else {
b[i] = 1e9;
}
}
printf("YES\n");
for (int i = 1; i <= n; i++){
printf("%d ", b[i]);
}
printf("\n");
}
int main(){
int t;
scanf("%d", &t);
for (int cas = 1; cas <= t; cas++){
solve();
}
return 0;
}