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.
Look at one column in isolation. After gravity, the bottom cell in column comes from the lowest array in the chosen order whose length is at least .
Now scan the chosen order from bottom to top. An array affects the bottom row only if its length is a new maximum; then it contributes exactly the suffix after the previous maximum length.
If columns are already fixed, and you next choose array with , then the remaining answer becomes followed by the optimal continuation from position . That gives a DP on the covered prefix length.
Process states from right to left. For an active array at position , to compare its candidate with another one you only need two things: the first value and the rank of the remaining tail, because all candidates have the same total length .
Maintain all arrays with as active. When moving from to , arrays with enter with tail-rank since their remainder is the optimal suffix from the next state. Sort active arrays by , compress equal pairs into equal ranks, and the rank- array is best[p]. Reconstruct by jumping .
Look at one column by itself. After all falling stops, the bottom cell in column is the element from the lowest array whose length is at least .
So if we read arrays from bottom to top, an array matters only when its length is a new maximum. If the previous maximum length was and this array has length , then it contributes exactly the suffix to the final bottom row.
Therefore every possible answer has the form
where
and .
Conversely, any such increasing-length chain is realizable: place those chosen arrays in that bottom-to-top order, and insert every unused array right above the first chosen array whose length is at least its own. Then it is never the lowest array in any column it occupies, so it changes nothing.
So yeah, the physics is fake. This is just lexicographic DP in a trench coat.
Let be the lexicographically minimum suffix of the bottom row after the first columns are already fixed.
If the next chosen array is with , then it contributes its suffix and afterwards we continue optimally from :
Brute force over all for every is too slow.
For a fixed state , define the candidate of array as
Among all active arrays (), the answer is just the minimum of these sequences.
Now process from down to .
Suppose we already know the relative order of all relevant tails at position . How do we compare candidates at position ?
All sequences have the same total length , so lexicographic comparison is simple:
So each active array is fully described by the pair
There are only two possibilities for that tail:
Therefore, at step :
Store that array as best[p].
We prove by descending induction on .
Assume that before processing , every active array stores the correct rank of its tail:
Then its full candidate at position is exactly
Because all have equal length, sorting by
is exactly the same as sorting by the full sequences lexicographically. After compression, the new stored rank is therefore the correct rank of .
So the first array after sorting is precisely the one minimizing , i.e. the one that gives . Induction done. No wizardry, just bookkeeping.
Start with .
If best[p] = i, then the optimal answer next uses array , so append
and jump to .
Repeat until .
Let be the number of active arrays at step .
At that step we sort items, which costs where
Also, each array is active for exactly steps, so
Hence the total complexity is
with memory.
Since , this is easily fast enough.
#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;
while (t--) {
int n;
cin >> n;
vector<vector<int>> a(n);
vector<int> k(n);
int L = 0;
for (int i = 0; i < n; ++i) {
cin >> k[i];
L = max(L, k[i]);
a[i].resize(k[i]);
for (int j = 0; j < k[i]; ++j) cin >> a[i][j];
}
vector<vector<int>> byLen(L + 1);
for (int i = 0; i < n; ++i) byLen[k[i]].push_back(i);
vector<int> active;
active.reserve(n);
vector<int> rk(n, 0);
vector<int> best(L, -1);
for (int p = L - 1; p >= 0; --p) {
for (int id : byLen[p + 1]) {
rk[id] = 1; // tail is F_{p+1}
active.push_back(id);
}
vector<array<int, 3>> ord;
ord.reserve(active.size());
for (int id : active) {
ord.push_back({a[id][p], rk[id], id});
}
sort(ord.begin(), ord.end(), [](const auto& x, const auto& y) {
if (x[0] != y[0]) return x[0] < y[0];
if (x[1] != y[1]) return x[1] < y[1];
return x[2] < y[2];
});
best[p] = ord[0][2];
int cur = 0;
int lastVal = -1, lastRank = -1;
for (auto &x : ord) {
if (x[0] != lastVal || x[1] != lastRank) {
++cur;
lastVal = x[0];
lastRank = x[1];
}
rk[x[2]] = cur;
}
}
vector<int> ans;
ans.reserve(L);
int p = 0;
while (p < L) {
int id = best[p];
for (int j = p; j < k[id]; ++j) ans.push_back(a[id][j]);
p = k[id];
}
for (int i = 0; i < L; ++i) {
if (i) cout << ' ';
cout << ans[i];
}
cout << '\n';
}
}#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;
while (t--) {
int n;
cin >> n;
vector<vector<int>> a(n);
vector<int> k(n);
int L = 0;
for (int i = 0; i < n; ++i) {
cin >> k[i];
L = max(L, k[i]);
a[i].resize(k[i]);
for (int j = 0; j < k[i]; ++j) cin >> a[i][j];
}
vector<vector<int>> byLen(L + 1);
for (int i = 0; i < n; ++i) byLen[k[i]].push_back(i);
vector<int> active;
active.reserve(n);
vector<int> rk(n, 0);
vector<int> best(L, -1);
for (int p = L - 1; p >= 0; --p) {
for (int id : byLen[p + 1]) {
rk[id] = 1; // tail is F_{p+1}
active.push_back(id);
}
vector<array<int, 3>> ord;
ord.reserve(active.size());
for (int id : active) {
ord.push_back({a[id][p], rk[id], id});
}
sort(ord.begin(), ord.end(), [](const auto& x, const auto& y) {
if (x[0] != y[0]) return x[0] < y[0];
if (x[1] != y[1]) return x[1] < y[1];
return x[2] < y[2];
});
best[p] = ord[0][2];
int cur = 0;
int lastVal = -1, lastRank = -1;
for (auto &x : ord) {
if (x[0] != lastVal || x[1] != lastRank) {
++cur;
lastVal = x[0];
lastRank = x[1];
}
rk[x[2]] = cur;
}
}
vector<int> ans;
ans.reserve(L);
int p = 0;
while (p < L) {
int id = best[p];
for (int j = p; j < k[id]; ++j) ans.push_back(a[id][j]);
p = k[id];
}
for (int i = 0; i < L; ++i) {
if (i) cout << ' ';
cout << ans[i];
}
cout << '\n';
}
}