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.
A query returns the left-to-right maxima of the queried indices. Those returned indices are already an increasing subsequence by value, because every new visible skyscraper is taller than all previous ones.
So if querying some set gives at least visible indices, you can instantly answer with the first of them. No need to know the actual permutation values.
If a query returns at most visible indices, remove exactly those indices and query the remaining indices again. Think of this as peeling the permutation into layers of left-to-right maxima.
After failed queries, each peeled layer had size at most , so at most indices were removed. Since there are indices, at least one index is still alive. Give alive indices layer .
The layer numbers secretly encode a decreasing subsequence. From a layer- index, the rightmost earlier layer- index must have a larger value. Scan indices right-to-left and pick layers ; reversing them gives a decreasing subsequence of length .
First, Kill the Wrong Assumption
This is interactive. The hidden permutation is not something your solution reads. In hacks, the permutation is given to the interactor, not to your program. Your program only gets query answers. So the job is not “compute LIS from the array”; the job is “force the interactor to reveal enough structure.”
Luckily, the query is absurdly useful: it returns left-to-right maxima.
If you query indices , the answer contains exactly those positions whose values beat every earlier queried value. Therefore the returned indices, in returned order, have strictly increasing values. So every query directly gives an increasing subsequence.
That is half the problem. The other half is what to do when every query is too short.
Peeling Layers
Maintain a sorted list alive of indices that have not been removed yet.
Initially, alive = [1,2,\ldots,n^2+1].
For rounds :
alive.alive.alive.This uses at most queries.
If we never stopped, then every queried visible layer had size at most . Across rounds, we removed at most
indices. But the permutation has indices, so at least one index remains alive. Assign every still-alive index layer .
Now we have layer labels available, and we need to turn them into a decreasing subsequence.
Why Layers Create a Decreasing Chain
Here is the key fact:
Suppose index survives round , meaning it is still alive while we query layer , but it is not returned as visible in that query. Then there is some earlier alive index with .
Why? Because if is not visible, some previous queried value is larger. Among all visible maxima before , take the last one. That index is returned in the query, so it belongs to layer , and its value is larger than .
Now apply this to an index in layer . It survived round , but was not removed during round , so the fact above says there is an earlier layer- index with a larger value.
Even better: the rightmost earlier layer- index works. The last visible maximum before in round is exactly the rightmost layer- index before ; if another layer- index were between them, it would also be visible and would be later. So we can reconstruct using only indices and layer numbers, no hidden values needed. Pretty slick, honestly.
Building the Decreasing Answer
Scan indices from right to left.
Start looking for layer . Pick the first one you see.
Then keep scanning left looking for layer , then , and so on down to .
This gives indices in decreasing index order, so reverse them before printing. After reversing, the indices are increasing. By the layer argument, the values strictly decrease from left to right, so this is a valid decreasing subsequence of length .
Correctness Proof
We prove the algorithm always outputs a valid monotone subsequence of length exactly .
First, if some query returns at least visible indices, those returned indices have strictly increasing values by definition of left-to-right maxima. Taking the first gives an increasing subsequence of length exactly .
Otherwise, every one of the queries returns at most indices. Thus the total number of removed indices is at most . Since there are total indices, at least one index remains after all rounds, so layer is nonempty.
Now consider any chosen index of layer . During round , was alive but was not visible; otherwise it would have been removed into layer . Therefore some earlier alive index had value greater than . The last visible maximum before in that round is a layer- index, lies before , and has value greater than . This is exactly the rightmost earlier layer- index.
So when we scan right-to-left and choose layers , every newly chosen index is earlier and has a larger value than the previous chosen index. After reversing the chosen list, indices increase and values strictly decrease. Therefore the output is a decreasing subsequence of length .
In all cases, the algorithm outputs a valid monotone subsequence of length exactly .
Complexity
The solution makes at most queries. Locally, it stores and filters indices per test case, so the local work is in the loose bound, easily fine for . Memory is .
Remember to flush after every query and final answer. endl is acceptable here because it flushes; this is one of the few times endl is not just performance cosplay.
#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;
int m = n * n + 1;
vector<int> alive(m);
iota(alive.begin(), alive.end(), 1);
vector<int> layer(m + 1, n);
auto query = [&](const vector<int>& v) {
cout << "? " << v.size();
for (int x : v) cout << ' ' << x;
cout << endl;
int c;
if (!(cin >> c) || c == -1) exit(0);
vector<int> res(c);
for (int &x : res) cin >> x;
return res;
};
bool answered = false;
for (int r = 0; r < n; r++) {
vector<int> visible = query(alive);
if ((int)visible.size() > n) {
cout << "!";
for (int i = 0; i <= n; i++) cout << ' ' << visible[i];
cout << endl;
answered = true;
break;
}
vector<char> removed(m + 1, 0);
for (int x : visible) {
removed[x] = 1;
layer[x] = r;
}
vector<int> nextAlive;
nextAlive.reserve(alive.size() - visible.size());
for (int x : alive) {
if (!removed[x]) nextAlive.push_back(x);
}
alive.swap(nextAlive);
}
if (answered) continue;
vector<int> ans;
int want = n;
for (int i = m; i >= 1 && want >= 0; i--) {
if (layer[i] == want) {
ans.push_back(i);
want--;
}
}
reverse(ans.begin(), ans.end());
cout << "!";
for (int x : ans) cout << ' ' << x;
cout << endl;
}
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;
while (T--) {
int n;
cin >> n;
int m = n * n + 1;
vector<int> alive(m);
iota(alive.begin(), alive.end(), 1);
vector<int> layer(m + 1, n);
auto query = [&](const vector<int>& v) {
cout << "? " << v.size();
for (int x : v) cout << ' ' << x;
cout << endl;
int c;
if (!(cin >> c) || c == -1) exit(0);
vector<int> res(c);
for (int &x : res) cin >> x;
return res;
};
bool answered = false;
for (int r = 0; r < n; r++) {
vector<int> visible = query(alive);
if ((int)visible.size() > n) {
cout << "!";
for (int i = 0; i <= n; i++) cout << ' ' << visible[i];
cout << endl;
answered = true;
break;
}
vector<char> removed(m + 1, 0);
for (int x : visible) {
removed[x] = 1;
layer[x] = r;
}
vector<int> nextAlive;
nextAlive.reserve(alive.size() - visible.size());
for (int x : alive) {
if (!removed[x]) nextAlive.push_back(x);
}
alive.swap(nextAlive);
}
if (answered) continue;
vector<int> ans;
int want = n;
for (int i = m; i >= 1 && want >= 0; i--) {
if (layer[i] == want) {
ans.push_back(i);
want--;
}
}
reverse(ans.begin(), ans.end());
cout << "!";
for (int x : ans) cout << ' ' << x;
cout << endl;
}
return 0;
}