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.
Ask for the sum of the whole current interval first. Flatten keeps an array's sum unchanged, and concatenate combines two arrays with equal sum, so total sums are powers of two with a very rigid half-and-half structure.
Any generated array of length more than has a unique place where the prefix sum is exactly half of the total sum. The elements are positive, so you can find that place with binary search on prefix sums.
That half-sum split is not random. The left and right parts came from the same earlier array, then each side was only flattened independently. No element ever crosses the split.
Flattening increases length by and never increases the maximum. So between the two half-sum parts, the shorter part was flattened fewer times and therefore contains a maximum of the whole interval. If the lengths tie, either side works.
Recurse only into that shorter half. The interval length at least halves each level, and each split is found by binary search, so the total number of queries is far below .
First, deconstruct the trap: this is interactive, but we are not trying to reconstruct the array. That would be massive overkill. We only need the maximum, and the operation history leaves enough structure to chase it with interval sums.
Flattening one element into two copies of preserves the sum of that array.
Concatenation sets both arrays to . Since and always had equal sums before the operation, the new array sum is exactly twice the old sum.
So every relevant array has a power-of-two total sum, and more importantly, whenever an array is split by its top-level construction, the two pieces have equal sum.
Think of a generated array as leaves of a binary structure.
A piece of total sum can be split into two consecutive pieces of sum in either of two ways:
After that split, later flatten operations only happen inside one side or the other. They preserve that side's sum and do not move elements across the boundary.
Therefore, for any generated interval with total sum and length greater than , there is an index such that
Because all are positive, prefix sums strictly increase, so this index is unique. Nice. The problem just handed us a binary-search target and tried to look mysterious about it.
Suppose we are currently solving interval and we know its total sum is .
We binary search the smallest position such that
Validity of the hidden array guarantees the sum is exactly at that position. Querying interval sums gives each binary-search comparison.
Now and both have total sum .
This is the key bit.
At the moment the two halves became separate, they came from the same state:
From the same starting array, every flatten operation increases length by exactly and never increases the maximum. It either leaves the maximum unchanged because other maximum elements remain, or it cuts the maximum in half. Either way, more flattening cannot make the maximum larger.
So the side with smaller length was flattened fewer times, and its maximum is at least the other side's maximum. That means it contains a maximum of the whole interval. If the lengths are equal, both sides have gone through the same number of flatten operations; either side is fine.
So after finding the half-sum split, recurse into the shorter side and throw away the longer side. Brutal, but correct.
For each test case:
Each recursion keeps an interval of length at most half the previous one. A binary search on length costs queries.
So the total is roughly
which is under for , plus the initial total-sum query. The limit is , so we are comfortably inside it.
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
void setIO() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
}
ll query(int l, int r) {
cout << "? " << l << ' ' << r << endl;
ll res;
if (!(cin >> res)) exit(0);
if (res == -1) exit(0);
return res;
}
int main() {
setIO();
int t;
cin >> t;
while (t--) {
int n;
cin >> n;
int l = 1, r = n;
ll sum = query(l, r);
while (l < r) {
ll half = sum / 2;
int lo = l, hi = r;
while (lo < hi) {
int mid = (lo + hi) / 2;
if (query(l, mid) >= half) {
hi = mid;
} else {
lo = mid + 1;
}
}
int cut = lo;
int left_len = cut - l + 1;
int right_len = r - cut;
if (left_len <= right_len) {
r = cut;
} else {
l = cut + 1;
}
sum = half;
}
cout << "! " << sum << endl;
}
return 0;
}#include <bits/stdc++.h>
using namespace std;
using ll = long long;
void setIO() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
}
ll query(int l, int r) {
cout << "? " << l << ' ' << r << endl;
ll res;
if (!(cin >> res)) exit(0);
if (res == -1) exit(0);
return res;
}
int main() {
setIO();
int t;
cin >> t;
while (t--) {
int n;
cin >> n;
int l = 1, r = n;
ll sum = query(l, r);
while (l < r) {
ll half = sum / 2;
int lo = l, hi = r;
while (lo < hi) {
int mid = (lo + hi) / 2;
if (query(l, mid) >= half) {
hi = mid;
} else {
lo = mid + 1;
}
}
int cut = lo;
int left_len = cut - l + 1;
int right_len = r - cut;
if (left_len <= right_len) {
r = cut;
} else {
l = cut + 1;
}
sum = half;
}
cout << "! " << sum << endl;
}
return 0;
}