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.
Let be the first position where differs from , and the last. Then the two comparisons only care about and .
For a fixed interval , rearranging only that interval can work iff neither endpoint is the maximum of .
If an interval works, the lexicographically smallest rearrangement puts the smallest value greater than first, then puts all remaining values in increasing order.
To minimize , push the first changed position as far right as possible. This position is one before the last local maximum of .
After the last local maximum, the suffix has no more peaks, so it is decreasing then increasing. That lets you sort the needed suffix with a merge, using only comparisons.
Let be the first index where differs from , and let be the last index where they differ. Then
is decided at , so we need . The reversed comparison is decided by the last changed original position, so we also need
Everything before and after stays unchanged. So the core task is: choose an interval and rearrange its values so both endpoints increase.
For a fixed interval , this is possible iff neither nor is the maximum of .
If is the maximum, position cannot be increased. Same for . Conversely, if neither endpoint is maximum, put the smallest value in the interval that is greater than at position , then put all remaining interval values in increasing order. The last position gets the largest remaining value. If the moved first value was not the maximum, the maximum lands at and beats . If it was the maximum, then was the second maximum, so the last value is , still larger than .
Now optimize lexicographically. The later the first changed position is, the smaller the whole permutation is. So we want the largest possible .
A valid interval exists starting at exactly when there is an internal maximum somewhere after it. In particular, if is a local maximum,
then works using the tiny interval .
Take the last local maximum . Then is optimal. No can work: after the last local maximum, the suffix has no peak, meaning its adjacent comparison pattern is some decreases followed by some increases. Any interval inside such a valley-shaped sequence has its maximum at an endpoint, which kills the endpoint-increase condition. No last local maximum means no answer. Simple, brutal, done.
Now we need construct the actual index permutation using at most queries.
Ask all adjacent comparisons and store
Then is the largest index with and . Let be the first changed position.
We need the values in sorted. Since there is no local maximum after , this suffix is decreasing, then increasing. So it is two sorted lists if we reverse the decreasing part:
Merge those two lists using comparison queries. This gives order, the indices sorted by their hidden values.
Now binary search in order for the first value greater than . That index becomes . Then replace that position inside order with , and write the rest of order after position . This is exactly: put the successor of first, then put all remaining suffix values in increasing order.
Query count:
That is safely within . Memory is .
#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;
auto ask = [&](int i, int j) -> int {
cout << "? " << i << ' ' << j << endl;
int ans;
cin >> ans;
if (ans == -1) exit(0);
return ans;
};
while (T--) {
int n;
cin >> n;
vector<int> up(n + 1, 1);
for (int i = 1; i < n; i++) {
up[i] = ask(i, i + 1);
}
int peak = -1;
for (int i = 2; i <= n - 1; i++) {
if (up[i - 1] && !up[i]) peak = i;
}
if (peak == -1) {
cout << "! -1" << endl;
continue;
}
int first = peak - 1;
int split = peak;
while (split + 1 <= n && !up[split + 1]) split++;
vector<int> order;
int l = split, r = split + 1;
while (l >= peak && r <= n) {
if (ask(l, r)) order.push_back(l--);
else order.push_back(r++);
}
while (l >= peak) order.push_back(l--);
while (r <= n) order.push_back(r++);
int lo = 0, hi = (int)order.size() - 1, take = -1;
while (lo <= hi) {
int mid = (lo + hi) / 2;
if (ask(first, order[mid])) {
take = mid;
hi = mid - 1;
} else {
lo = mid + 1;
}
}
vector<int> ans(n + 1);
iota(ans.begin(), ans.end(), 0);
ans[first] = order[take];
order[take] = first;
for (int i = 0; i < (int)order.size(); i++) {
ans[first + 1 + i] = order[i];
}
cout << "!";
for (int i = 1; i <= n; i++) cout << ' ' << ans[i];
cout << endl;
}
}#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;
auto ask = [&](int i, int j) -> int {
cout << "? " << i << ' ' << j << endl;
int ans;
cin >> ans;
if (ans == -1) exit(0);
return ans;
};
while (T--) {
int n;
cin >> n;
vector<int> up(n + 1, 1);
for (int i = 1; i < n; i++) {
up[i] = ask(i, i + 1);
}
int peak = -1;
for (int i = 2; i <= n - 1; i++) {
if (up[i - 1] && !up[i]) peak = i;
}
if (peak == -1) {
cout << "! -1" << endl;
continue;
}
int first = peak - 1;
int split = peak;
while (split + 1 <= n && !up[split + 1]) split++;
vector<int> order;
int l = split, r = split + 1;
while (l >= peak && r <= n) {
if (ask(l, r)) order.push_back(l--);
else order.push_back(r++);
}
while (l >= peak) order.push_back(l--);
while (r <= n) order.push_back(r++);
int lo = 0, hi = (int)order.size() - 1, take = -1;
while (lo <= hi) {
int mid = (lo + hi) / 2;
if (ask(first, order[mid])) {
take = mid;
hi = mid - 1;
} else {
lo = mid + 1;
}
}
vector<int> ans(n + 1);
iota(ans.begin(), ans.end(), 0);
ans[first] = order[take];
order[take] = first;
for (int i = 0; i < (int)order.size(); i++) {
ans[first + 1 + i] = order[i];
}
cout << "!";
for (int i = 1; i <= n; i++) cout << ' ' << ans[i];
cout << endl;
}
}