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 not try to control the ordinary number of inversion pairs. This value counts whole subsegments, so one inversion can affect many subsegments.
Count the opposite thing: subsegments with no inversion. A subsegment of a permutation has no inversion exactly when it is strictly increasing.
Split the permutation into maximal contiguous increasing runs. If a run has length , it contributes inversion-free subsegments.
Let . You need the number of inversion-free subsegments to be , so the task becomes: split into positive run lengths such that .
Use DP over (used length, current sum) to find those run lengths. Then realize them by outputting each run in increasing order, but using larger values for earlier runs than later runs, forcing descents between runs.
First, kill the wrong assumption: this is not asking for a permutation with exactly inversion pairs. It asks for exactly subsegments that contain at least one inversion. One inversion can poison a bunch of subsegments, so direct inversion-count tricks are the wrong tool.
Flip the Count
There are exactly
subsegments with length at least , because choosing endpoints with defines one.
Instead of counting subsegments that contain an inversion, count subsegments that contain no inversion.
A subsegment has no inversion if and only if it is strictly increasing. Since all values are distinct, this is clean: if the segment is increasing, no pair is inverted; if it is not increasing, some adjacent pair drops, and that adjacent pair is already an inversion.
So if the desired inversion value is , then the number of increasing subsegments of length at least must be
.
Increasing Runs
Now look at the permutation as maximal contiguous increasing runs.
Example:
3 1 4 2
has runs:
[3], [1 4], [2]
Only subsegments fully inside one run are inversion-free. Any subsegment crossing a run boundary contains a descent at that boundary, so it definitely contains an inversion.
If a run has length , the number of length-at-least-2 subsegments inside it is
.
Therefore, if the run lengths are , then
.
Also, of course,
.
So the whole problem becomes this much smaller problem:
Find positive integers summing to such that
.
If impossible, output 0.
DP for Run Lengths
Since , . Tiny. We can use DP.
Let mean we can choose some run lengths whose total length is used and whose triangular contribution is sum.
Transition: choose the next run length len, where used + len <= n. It adds
to the sum.
We store the chosen len as a parent pointer so we can reconstruct the run lengths if is reachable.
Building the Permutation
Suppose DP gives run lengths like:
.
We need a permutation whose maximal increasing runs have exactly those lengths.
The easy construction:
For example, with and run lengths [2,4]:
5 61 2 3 4Permutation:
5 6 1 2 3 4
Inside each block, values increase. Between blocks, the last value of the earlier block is larger than the first value of the next block, so there is a descent. That means the maximal increasing runs are exactly the blocks we asked for.
No hidden magic, just force the run structure and let the formula do the counting.
Correctness Proof
Consider any subsegment. It has no inversion if and only if all its values are strictly increasing in order. If it is not strictly increasing, then at some adjacent position inside the subsegment there is a descent, which is an inversion. So inversion-free subsegments are exactly increasing subsegments.
Now split a permutation into maximal increasing runs. Every subsegment fully inside one run is increasing, so a run of length contributes inversion-free subsegments. Any subsegment crossing between two runs contains the descent at that boundary, so it is not inversion-free. Therefore the total number of inversion-free subsegments is exactly the sum of over all run lengths.
The DP considers every possible sequence of positive run lengths summing to at most , tracking exactly this sum of triangular contributions. Thus, if it reaches state , the reconstructed lengths describe a valid run structure with exactly inversion-free subsegments. If it cannot reach that state, no permutation can work, because every valid permutation would produce such a list of run lengths.
Finally, the construction realizes the chosen run lengths exactly: each block is internally increasing, and every earlier block uses larger numbers than every later block, so every boundary between blocks is a descent. Hence the maximal increasing runs are exactly the chosen blocks. The inversion value is therefore
.
So the algorithm is correct.
Complexity
The DP has at most states and tries at most run lengths per state. This is tiny. Per test case, the complexity is , easily within limits.
#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 tc;
cin >> tc;
while (tc--) {
int n, k;
cin >> n >> k;
int total = n * (n - 1) / 2;
int need = total - k;
vector<vector<int>> parent(n + 1, vector<int>(need + 1, -1));
parent[0][0] = 0;
for (int used = 0; used <= n; used++) {
for (int sum = 0; sum <= need; sum++) {
if (parent[used][sum] == -1) continue;
for (int len = 1; used + len <= n; len++) {
int add = len * (len - 1) / 2;
if (sum + add <= need && parent[used + len][sum + add] == -1) {
parent[used + len][sum + add] = len;
}
}
}
}
if (parent[n][need] == -1) {
cout << 0 << '\n';
continue;
}
vector<int> runs;
int used = n, sum = need;
while (used > 0) {
int len = parent[used][sum];
runs.push_back(len);
used -= len;
sum -= len * (len - 1) / 2;
}
reverse(runs.begin(), runs.end());
vector<int> ans;
int hi = n;
for (int len : runs) {
int lo = hi - len + 1;
for (int x = lo; x <= hi; x++) ans.push_back(x);
hi -= len;
}
for (int i = 0; i < n; 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 tc;
cin >> tc;
while (tc--) {
int n, k;
cin >> n >> k;
int total = n * (n - 1) / 2;
int need = total - k;
vector<vector<int>> parent(n + 1, vector<int>(need + 1, -1));
parent[0][0] = 0;
for (int used = 0; used <= n; used++) {
for (int sum = 0; sum <= need; sum++) {
if (parent[used][sum] == -1) continue;
for (int len = 1; used + len <= n; len++) {
int add = len * (len - 1) / 2;
if (sum + add <= need && parent[used + len][sum + add] == -1) {
parent[used + len][sum + add] = len;
}
}
}
}
if (parent[n][need] == -1) {
cout << 0 << '\n';
continue;
}
vector<int> runs;
int used = n, sum = need;
while (used > 0) {
int len = parent[used][sum];
runs.push_back(len);
used -= len;
sum -= len * (len - 1) / 2;
}
reverse(runs.begin(), runs.end());
vector<int> ans;
int hi = n;
for (int len : runs) {
int lo = hi - len + 1;
for (int x = lo; x <= hi; x++) ans.push_back(x);
hi -= len;
}
for (int i = 0; i < n; i++) {
if (i) cout << ' ';
cout << ans[i];
}
cout << '\n';
}
}