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.
Do the forward problem first. For a fixed permutation, the optimal partition is a chain of starting indices, not arbitrary DP soup.
From a current start , let be the first later position with . The next start must be the maximum value among positions through (or through the end if no such exists).
For every prefix, that chain ends at the last index. If , the predecessor of in the chain is the latest earlier index with . So is really a preorder list of tree depths.
The trap: a branching node cannot have a branching child. The tiny bad case already shows why; it forces one long increasing comparison chain and destroys the intended depths.
Once you have that tree, construct values recursively. Branching nodes are made the minimum of their subtree, their child roots get the largest values from left to right, and single-child nodes pass either the smallest or largest remaining value depending on whether the child branches.
The annoying part is that is not asking for the number of blocks in some random optimal partition. The lexicographic maximum forces a very specific chain of starting positions. Once you see that chain, the problem turns into a tree construction.
Computing the optimal chain
Take a prefix , and suppose the current segment starts at position .
Let be the first position after with . If there is no such position, pretend for the allowed range.
The first minimum in the answer should be as large as possible. We can always make it by starting the block at , but if the block includes the first smaller element, the minimum drops below , which is instantly worse. So the first block must end before that first smaller element.
After fixing the first output value as , we choose where the next block starts. If it starts at , then the next output value starts with . So among all legal next starts, we choose the position with maximum value. Then we repeat from there.
So the optimal sequence is a chain of indices:
That last bullet matters: for any prefix of length , the chain always ends at index . Therefore is exactly the length of this chain.
The false assumption is that appending one element only increases the answer by or . Nope. A very large new value can make an early chain node jump straight to the end and delete a bunch of old chain nodes from the optimal answer. That is where the problem earns its 3500 tax.
Turning into a tree
If , then in the optimal chain for prefix , index is the last node and its predecessor has chain length .
More specifically, the predecessor is the latest earlier index with . If a later such index existed, the jump rule would prefer that later candidate structure instead. So each index gets a parent:
latest such that .
Now is a preorder depth list of a rooted tree:
Immediate impossible cases:
The real obstruction
Call a node branching if it has at least two children.
A branching node cannot have a branching child.
Why? Suppose node has multiple children. For the chain to jump from to its next child, that child root must beat everything relevant before it. This forces the child roots of to behave like increasing maxima, and values in those child areas must stay above .
Now if one child also branches, its own children force the same kind of increasing-maxima behavior below . Then a later grandchild becomes larger than while still being visible from . The jump from would go too deep, skipping , so the depth sequence collapses. The classic tiny disaster is : it forces comparisons like and , so everything becomes one increasing run, giving the wrong .
So the tree is valid exactly when no branching node has a branching child.
Constructing the permutation
We recursively assign a consecutive value interval to each subtree. The subtree of a node is contiguous because the indices are preorder labels.
For a subtree rooted at , suppose all still-unassigned values for descendants are in , and is already assigned.
There are two cases.
Case 1: has one child .
There is no sibling competition, so the chain from only needs to go to . If is branching, give the smallest remaining value, because a branching node wants to be the minimum of its own subtree. Otherwise, give the largest remaining value, which keeps chains simple. Then recurse on .
Case 2: has multiple children.
Then must be the minimum of its subtree. If this node was forbidden to branch, we fail.
Give the direct children the largest available values, increasing from left to right. Give each child subtree a consecutive block of the smaller remaining values. Then recurse into every child, marking those children as not allowed to branch. This exactly enforces the no branching child of a branching node rule.
For the root:
The recursive interval assignment uses every number exactly once, so the output is a permutation. The induction proof follows the construction: single-child nodes have only one possible next tree step, and branching nodes see their direct child roots as the left-to-right maxima, while descendants are kept below those roots.
Everything is linear: build the tree in , assign values in , done.
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
void setIO() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
}
struct Frame {
int u, r, lo, hi;
bool canBranch;
};
int main() {
setIO();
int T;
cin >> T;
while (T--) {
int n;
cin >> n;
vector<int> x(n);
for (int &v : x) cin >> v;
bool ok = true;
vector<vector<int>> child(n);
vector<int> last(n + 2, -1);
if (x[0] != 1) ok = false;
if (ok) last[1] = 0;
for (int i = 1; i < n && ok; ++i) {
int d = x[i];
if (d <= 1 || d > n || d > x[i - 1] + 1 || last[d - 1] == -1) {
ok = false;
break;
}
int p = last[d - 1];
child[p].push_back(i);
last[d] = i;
}
vector<int> a(n, 0);
if (ok) {
vector<Frame> st;
if ((int)child[0].size() > 1) {
a[0] = 1;
st.push_back({0, n - 1, 2, n, true});
} else {
a[0] = n;
st.push_back({0, n - 1, 1, n - 1, true});
}
while (!st.empty() && ok) {
auto [u, r, lo, hi, canBranch] = st.back();
st.pop_back();
if (u == r) continue;
auto &ch = child[u];
if (ch.empty() || ch[0] != u + 1) {
ok = false;
break;
}
int cntChild = (int)ch.size();
if (!canBranch && cntChild != 1) {
ok = false;
break;
}
if (cntChild == 1) {
int v = ch[0];
if (lo > hi) {
ok = false;
break;
}
if ((int)child[v].size() > 1) a[v] = lo++;
else a[v] = hi--;
st.push_back({v, r, lo, hi, true});
} else {
if (hi - lo + 1 < cntChild) {
ok = false;
break;
}
int firstRootValue = hi - cntChild + 1;
int remainingHi = hi - cntChild;
int cur = lo;
for (int i = 0; i < cntChild; ++i) {
int v = ch[i];
a[v] = firstRootValue + i;
int childR = (i + 1 < cntChild ? ch[i + 1] - 1 : r);
int descendants = childR - v;
st.push_back({v, childR, cur, cur + descendants - 1, false});
cur += descendants;
}
if (cur != remainingHi + 1) ok = false;
}
}
}
if (!ok) {
cout << "NO\n";
} else {
cout << "YES\n";
for (int i = 0; i < n; ++i) {
cout << a[i] << (i + 1 == n ? '\n' : ' ');
}
}
}
return 0;
}#include <bits/stdc++.h>
using namespace std;
using ll = long long;
void setIO() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
}
struct Frame {
int u, r, lo, hi;
bool canBranch;
};
int main() {
setIO();
int T;
cin >> T;
while (T--) {
int n;
cin >> n;
vector<int> x(n);
for (int &v : x) cin >> v;
bool ok = true;
vector<vector<int>> child(n);
vector<int> last(n + 2, -1);
if (x[0] != 1) ok = false;
if (ok) last[1] = 0;
for (int i = 1; i < n && ok; ++i) {
int d = x[i];
if (d <= 1 || d > n || d > x[i - 1] + 1 || last[d - 1] == -1) {
ok = false;
break;
}
int p = last[d - 1];
child[p].push_back(i);
last[d] = i;
}
vector<int> a(n, 0);
if (ok) {
vector<Frame> st;
if ((int)child[0].size() > 1) {
a[0] = 1;
st.push_back({0, n - 1, 2, n, true});
} else {
a[0] = n;
st.push_back({0, n - 1, 1, n - 1, true});
}
while (!st.empty() && ok) {
auto [u, r, lo, hi, canBranch] = st.back();
st.pop_back();
if (u == r) continue;
auto &ch = child[u];
if (ch.empty() || ch[0] != u + 1) {
ok = false;
break;
}
int cntChild = (int)ch.size();
if (!canBranch && cntChild != 1) {
ok = false;
break;
}
if (cntChild == 1) {
int v = ch[0];
if (lo > hi) {
ok = false;
break;
}
if ((int)child[v].size() > 1) a[v] = lo++;
else a[v] = hi--;
st.push_back({v, r, lo, hi, true});
} else {
if (hi - lo + 1 < cntChild) {
ok = false;
break;
}
int firstRootValue = hi - cntChild + 1;
int remainingHi = hi - cntChild;
int cur = lo;
for (int i = 0; i < cntChild; ++i) {
int v = ch[i];
a[v] = firstRootValue + i;
int childR = (i + 1 < cntChild ? ch[i + 1] - 1 : r);
int descendants = childR - v;
st.push_back({v, childR, cur, cur + descendants - 1, false});
cur += descendants;
}
if (cur != remainingHi + 1) ok = false;
}
}
}
if (!ok) {
cout << "NO\n";
} else {
cout << "YES\n";
for (int i = 0; i < n; ++i) {
cout << a[i] << (i + 1 == n ? '\n' : ' ');
}
}
}
return 0;
}