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 min-max expression is just an ordered tree. First flatten consecutive equal operations, so every edge alternates between and .
Try the query . Since the values are strictly increasing, a node always chooses its rightmost child, and a node always chooses its leftmost child.
If the answer on an interval is , then lies on the alternating spine. If or , one side can be peeled off by replacing that endpoint with or .
For an interior , recover the left projection by setting , and the right projection by setting . Both projections contain the same middle leaf .
After recursion, left pieces attach only by and right pieces attach only by . To decide which adjacent piece is closer to , query values for far-left, left-candidate, middle, right-candidate, far-right. Result means add the left piece; result means add the right piece.
Normalize the expression tree by flattening any node whose child has the same operation. Now operations alternate along every edge, leaves are in order, and every query result is one of the queried values. That last fact is the whole game: Bowser is not returning mystery sauce, he is returning a winning leaf.
Consider a subproblem on a contiguous interval , while variables outside it are fixed to defaults that make them harmless. Ask
Because , a node picks its rightmost child, and a node picks its leftmost child. So the answer is the leaf reached by following this alternating rule. Call it the spine leaf.
If , the current structure ends by attaching through a layer. We can neutralize it with , recursively recover , and return
Similarly, if , we neutralize with , recover , and return
These cases are the easy lava platforms. Step on them and move on.
Now suppose . The leaf has relevant structure on both sides. The trick is to make one side disappear without damaging the spine.
For the left projection, set every variable after to . Those right-side pieces are attached through layers, so is neutral. Recursively recover .
For the right projection, set every variable before to . Those left-side pieces are attached through layers, so is neutral. Recursively recover .
After flattening, the left recovered tree ends with the leaf , and the right recovered tree starts with the same leaf . Remove those duplicate copies. What remains is a list of left pieces and a list of right pieces that must be wrapped around the middle.
Let be the middle tree already built, the nearest unused left piece, and the nearest unused right piece. We know attaches by and attaches by ; the only question is which one is closer to .
Set values like this:
for far-left pieces, , , , and far-right pieces. Since all leaves inside one piece receive the same value, that whole piece evaluates to that value.
If is closer, locally we see
so the answer is , and we add by .
If is closer, locally we see
so the answer is , and we add by .
Once one side is empty, all remaining pieces on the other side attach in the only possible way. No more detective work; just stack them.
Each recursive discovery query or merge query is charged to a constant-size piece of the recovered tree, so the total number of interaction queries is linear:
With , this stays inside the query budget. After reconstruction, each of Bowser's online queries is answered by evaluating the recovered tree in
time. Cross-checked against the official Codeforces editorial: https://codeforces.com/blog/entry/151886.
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
void setIO() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
}
const int SHIFT = 100000;
const int INFV = 100000;
int n;
int readInt() {
int x;
if (!(cin >> x)) exit(0);
if (x == -1) exit(0);
return x;
}
struct Node {
int l, r;
bool isMax, leaf;
vector<Node*> child;
Node(int L, int R) : l(L), r(R), isMax(false), leaf(true) {}
Node(int L, int R, bool op, vector<Node*> kids)
: l(L), r(R), isMax(op), leaf(false), child(kids) {}
void flatten() {
if (leaf) return;
vector<Node*> nxt;
for (Node* v : child) {
v->flatten();
if (!v->leaf && v->isMax == isMax) {
for (Node* u : v->child) nxt.push_back(u);
} else {
nxt.push_back(v);
}
}
child.swap(nxt);
}
int eval(const vector<int>& x) const {
if (leaf) return x[l];
int ans = isMax ? INT_MIN : INT_MAX;
for (Node* v : child) {
int got = v->eval(x);
if (isMax) ans = max(ans, got);
else ans = min(ans, got);
}
return ans;
}
};
int ask(const vector<int>& q) {
cout << "?";
for (int i = 1; i <= n; i++) {
int v = q[i];
v = max(-SHIFT, min(SHIFT, v));
cout << ' ' << v + SHIFT + 1;
}
cout << endl;
int ans = readInt();
return ans - SHIFT - 1;
}
Node* recover(int l, int r, const vector<int>& defaults) {
if (l == r) return new Node(l, r);
vector<int> q = defaults;
for (int i = l; i <= r; i++) q[i] = i;
int p = ask(q);
if (p == r) {
vector<int> nd = defaults;
nd[r] = -INFV;
Node* rest = recover(l, r - 1, nd);
Node* res = new Node(l, r, true, {rest, new Node(r, r)});
res->flatten();
return res;
}
if (p == l) {
vector<int> nd = defaults;
nd[l] = INFV;
Node* rest = recover(l + 1, r, nd);
Node* res = new Node(l, r, false, {new Node(l, l), rest});
res->flatten();
return res;
}
vector<int> leftDefaults = defaults;
for (int i = p + 1; i <= r; i++) leftDefaults[i] = INFV;
vector<int> rightDefaults = defaults;
for (int i = l; i < p; i++) rightDefaults[i] = -INFV;
Node* left = recover(l, p, leftDefaults);
left->flatten();
vector<Node*> leftPieces = left->child;
leftPieces.pop_back();
Node* right = recover(p, r, rightDefaults);
right->flatten();
vector<Node*> rightPieces = right->child;
reverse(rightPieces.begin(), rightPieces.end());
rightPieces.pop_back();
Node* mid = new Node(p, p);
while (!leftPieces.empty() && !rightPieces.empty()) {
Node* A = leftPieces.back();
Node* B = rightPieces.back();
vector<int> query = defaults;
for (int i = l; i < A->l; i++) query[i] = 0;
for (int i = A->l; i <= A->r; i++) query[i] = 3;
for (int i = A->r + 1; i < B->l; i++) query[i] = 2;
for (int i = B->l; i <= B->r; i++) query[i] = 1;
for (int i = B->r + 1; i <= r; i++) query[i] = 4;
int got = ask(query);
if (got == 1) {
mid = new Node(A->l, mid->r, true, {A, mid});
mid->flatten();
leftPieces.pop_back();
} else {
mid = new Node(mid->l, B->r, false, {mid, B});
mid->flatten();
rightPieces.pop_back();
}
}
while (!leftPieces.empty()) {
Node* A = leftPieces.back();
leftPieces.pop_back();
mid = new Node(A->l, mid->r, true, {A, mid});
mid->flatten();
}
while (!rightPieces.empty()) {
Node* B = rightPieces.back();
rightPieces.pop_back();
mid = new Node(mid->l, B->r, false, {mid, B});
mid->flatten();
}
return mid;
}
int main() {
setIO();
int T = readInt();
while (T--) {
n = readInt();
vector<int> defaults(n + 1, 0);
Node* root = recover(1, n, defaults);
cout << "!" << endl;
while (true) {
vector<int> x(n + 1);
x[1] = readInt();
if (x[1] == 0) break;
for (int i = 2; i <= n; i++) x[i] = readInt();
cout << root->eval(x) << endl;
}
}
return 0;
}#include <bits/stdc++.h>
using namespace std;
using ll = long long;
void setIO() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
}
const int SHIFT = 100000;
const int INFV = 100000;
int n;
int readInt() {
int x;
if (!(cin >> x)) exit(0);
if (x == -1) exit(0);
return x;
}
struct Node {
int l, r;
bool isMax, leaf;
vector<Node*> child;
Node(int L, int R) : l(L), r(R), isMax(false), leaf(true) {}
Node(int L, int R, bool op, vector<Node*> kids)
: l(L), r(R), isMax(op), leaf(false), child(kids) {}
void flatten() {
if (leaf) return;
vector<Node*> nxt;
for (Node* v : child) {
v->flatten();
if (!v->leaf && v->isMax == isMax) {
for (Node* u : v->child) nxt.push_back(u);
} else {
nxt.push_back(v);
}
}
child.swap(nxt);
}
int eval(const vector<int>& x) const {
if (leaf) return x[l];
int ans = isMax ? INT_MIN : INT_MAX;
for (Node* v : child) {
int got = v->eval(x);
if (isMax) ans = max(ans, got);
else ans = min(ans, got);
}
return ans;
}
};
int ask(const vector<int>& q) {
cout << "?";
for (int i = 1; i <= n; i++) {
int v = q[i];
v = max(-SHIFT, min(SHIFT, v));
cout << ' ' << v + SHIFT + 1;
}
cout << endl;
int ans = readInt();
return ans - SHIFT - 1;
}
Node* recover(int l, int r, const vector<int>& defaults) {
if (l == r) return new Node(l, r);
vector<int> q = defaults;
for (int i = l; i <= r; i++) q[i] = i;
int p = ask(q);
if (p == r) {
vector<int> nd = defaults;
nd[r] = -INFV;
Node* rest = recover(l, r - 1, nd);
Node* res = new Node(l, r, true, {rest, new Node(r, r)});
res->flatten();
return res;
}
if (p == l) {
vector<int> nd = defaults;
nd[l] = INFV;
Node* rest = recover(l + 1, r, nd);
Node* res = new Node(l, r, false, {new Node(l, l), rest});
res->flatten();
return res;
}
vector<int> leftDefaults = defaults;
for (int i = p + 1; i <= r; i++) leftDefaults[i] = INFV;
vector<int> rightDefaults = defaults;
for (int i = l; i < p; i++) rightDefaults[i] = -INFV;
Node* left = recover(l, p, leftDefaults);
left->flatten();
vector<Node*> leftPieces = left->child;
leftPieces.pop_back();
Node* right = recover(p, r, rightDefaults);
right->flatten();
vector<Node*> rightPieces = right->child;
reverse(rightPieces.begin(), rightPieces.end());
rightPieces.pop_back();
Node* mid = new Node(p, p);
while (!leftPieces.empty() && !rightPieces.empty()) {
Node* A = leftPieces.back();
Node* B = rightPieces.back();
vector<int> query = defaults;
for (int i = l; i < A->l; i++) query[i] = 0;
for (int i = A->l; i <= A->r; i++) query[i] = 3;
for (int i = A->r + 1; i < B->l; i++) query[i] = 2;
for (int i = B->l; i <= B->r; i++) query[i] = 1;
for (int i = B->r + 1; i <= r; i++) query[i] = 4;
int got = ask(query);
if (got == 1) {
mid = new Node(A->l, mid->r, true, {A, mid});
mid->flatten();
leftPieces.pop_back();
} else {
mid = new Node(mid->l, B->r, false, {mid, B});
mid->flatten();
rightPieces.pop_back();
}
}
while (!leftPieces.empty()) {
Node* A = leftPieces.back();
leftPieces.pop_back();
mid = new Node(A->l, mid->r, true, {A, mid});
mid->flatten();
}
while (!rightPieces.empty()) {
Node* B = rightPieces.back();
rightPieces.pop_back();
mid = new Node(mid->l, B->r, false, {mid, B});
mid->flatten();
}
return mid;
}
int main() {
setIO();
int T = readInt();
while (T--) {
n = readInt();
vector<int> defaults(n + 1, 0);
Node* root = recover(1, n, defaults);
cout << "!" << endl;
while (true) {
vector<int> x(n + 1);
x[1] = readInt();
if (x[1] == 0) break;
for (int i = 2; i <= n; i++) x[i] = readInt();
cout << root->eval(x) << endl;
}
}
return 0;
}