Search for a command to run...
Progressive hints first, then the full explanation and implementation when you're ready to cash out.
Review status
AI-generated and still unreviewed. Double-check the details before internalizing them.
Hints
Open only as much as you need to keep the solve alive.
For prefix , translate “ is the -mex” into a count. Exactly how many numbers smaller than must still be missing?
That count immediately traps every value:
Also, after adding one element while the requested rank decreases by , the answer can never increase. So must be non-increasing.
Use a fake starting value . Before inserting anything, the -mex of the empty set is , so this fake value makes the first step behave like all the others.
If , insert a huge dummy like . It changes none of the small missing numbers; the rank shift does the drop for free. If , you must insert one useful missing number below it.
Mark every value appearing in as forbidden. On equal steps , output the largest unused number in that is not forbidden. On strict drops, output . The only checks needed are and .
At prefix , is the -mex. So exactly numbers smaller than are missing, and itself is missing.
There are only numbers below , hence
Also, after insertions, at most distinct numbers below can be present. The number present below must be
so it cannot exceed . Therefore
Thus every valid sequence satisfies
With -indexed code, this is .
From prefix to prefix , we add one number, while the requested missing rank decreases by :
Removing at most one missing number cannot make this lower rank point to a larger value. So we also need
Define a fake value . Before inserting anything, the -mex of the empty set is , so this makes the first real step fit the same rule.
Call a value forbidden if it appears in . We never output forbidden values, because if for some , then must still be missing at prefix . Printing it would instantly nuke the solution. Very efficient, very wrong.
Maintain a pointer from down to , skipping forbidden values.
For each :
Why this works:
For the clean invariant, after processing prefix , exactly
inserted useful values are below . Therefore the number of missing values below is
And itself is missing because it is forbidden. Hence is exactly the -mex.
The pointer never runs out: every strict drop introduces one new forbidden value below , and every non-drop step consumes one non-forbidden value. The counts match perfectly. Cute little bookkeeping trick, no wizardry.
Each value is skipped by the pointer at most once, so the whole construction is linear:
per test case, and
over all test cases. Memory usage is .
Cross-checked against the official Codeforces editorial: https://codeforces.com/blog/entry/151886.
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
void setIO() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
}
int main() {
setIO();
int T;
cin >> T;
const int INF = 1000000000;
while (T--) {
int n;
cin >> n;
vector<ll> a(n);
for (ll &x : a) cin >> x;
bool ok = true;
vector<char> forbidden(n + 1, false);
for (int i = 0; i < n; i++) {
if (a[i] < n - i - 1 || a[i] > n) ok = false;
if (i > 0 && a[i] > a[i - 1]) ok = false;
if (0 <= a[i] && a[i] <= n) forbidden[(int)a[i]] = true;
}
if (!ok) {
cout << "NO\n";
continue;
}
vector<int> b;
b.reserve(n);
int ptr = n;
ll prev = n;
for (int i = 0; i < n; i++) {
if (prev > a[i]) {
b.push_back(INF);
} else {
do {
--ptr;
} while (ptr >= 0 && forbidden[ptr]);
b.push_back(ptr);
}
prev = a[i];
}
cout << "YES\n";
for (int i = 0; i < n; i++) {
if (i) cout << ' ';
cout << b[i];
}
cout << '\n';
}
return 0;
}#include <bits/stdc++.h>
using namespace std;
using ll = long long;
void setIO() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
}
int main() {
setIO();
int T;
cin >> T;
const int INF = 1000000000;
while (T--) {
int n;
cin >> n;
vector<ll> a(n);
for (ll &x : a) cin >> x;
bool ok = true;
vector<char> forbidden(n + 1, false);
for (int i = 0; i < n; i++) {
if (a[i] < n - i - 1 || a[i] > n) ok = false;
if (i > 0 && a[i] > a[i - 1]) ok = false;
if (0 <= a[i] && a[i] <= n) forbidden[(int)a[i]] = true;
}
if (!ok) {
cout << "NO\n";
continue;
}
vector<int> b;
b.reserve(n);
int ptr = n;
ll prev = n;
for (int i = 0; i < n; i++) {
if (prev > a[i]) {
b.push_back(INF);
} else {
do {
--ptr;
} while (ptr >= 0 && forbidden[ptr]);
b.push_back(ptr);
}
prev = a[i];
}
cout << "YES\n";
for (int i = 0; i < n; i++) {
if (i) cout << ' ';
cout << b[i];
}
cout << '\n';
}
return 0;
}