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.
Forget inversions for a second. What happens to the inversion count modulo after one left rotation? Track only the character that moves from front to back.
If the substring has length and contains ones, consecutive rotation inversion counts are congruent modulo with common difference . So is really wearing a fake mustache.
For an even-length substring, the answer reveals the parity of the number of ones in it: .
Define prefix parities . An even-length query gives . Notice that and always have the same parity.
Build one spanning tree on even prefix indices and one on odd prefix indices. Query long edges only: in each parity class, connect every node to the farther endpoint of that class. Set , arbitrarily set , reconstruct one string, and use its complement as the second guess.
Take a substring of length with ones, and let be the inversion count of the -th left rotation.
When the first character is moved to the end:
So always
The values in form an arithmetic progression modulo with step . A step- walk on a cycle of size visits exactly
different residues. Therefore
That is the first slap in the face: the inversion circus only tells us a gcd.
Suppose is even. From the answer we know
Because is even, is odd exactly when is odd. Hence
So any even-length query tells us the XOR of the bits in that substring.
Let
For an even-length query , the number of ones in the substring modulo is
Also, since is even, the two prefix indices and have the same parity. So queries only connect even prefix indices to even prefix indices, and odd prefix indices to odd prefix indices. The two worlds never talk. Annoying, but useful.
is fixed as , so the even prefix component has an absolute value. But is unknown, so the odd component can be flipped entirely.
If we choose , we reconstruct one candidate. If the real value was , then every odd prefix parity is toggled. Since every bit is
and adjacent prefix indices have opposite parity, toggling all odd prefixes flips every bit. Therefore the only ambiguity is between a string and its complement. The statement allows two guesses, so this is not a bug, it is the intended loophole.
For each parity , consider the prefix indices
where is the largest prefix index at most with parity .
For every node in this parity class, connect to whichever endpoint, or , is farther away. If the edge is between prefix indices , ask the substring . Its length is even, so the returned value gives the edge label
This graph is connected: connects to , and every other node connects to either or . So we can DFS/BFS and recover all prefix parities, up to the single odd-component flip described above.
In one parity class, write . There are queried edges. For node , the queried length is
Since , we have
Thus the total cost inside this parity class is at most
There are two parity classes, so the total query cost is at most
We use exactly queries and at most guesses.
The number of queries is , the local computation is , and the memory usage is . The cost budget is bounded by .
Cross-check sources: Codeforces problem statement and official Codeforces tutorial.
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
void setIO() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
}
int ask(int u, int v) {
if (u > v) swap(u, v);
int m = v - u;
cout << '?' << ' ' << u + 1 << ' ' << v << endl;
int f;
if (!(cin >> f)) exit(0);
if (f == -1) exit(0);
return (m / f) & 1;
}
void solve() {
int n;
cin >> n;
vector<vector<pair<int, int>>> g(n + 1);
for (int parity = 0; parity < 2; parity++) {
int last = n;
if ((last & 1) != parity) last--;
for (int x = parity + 2; x <= last; x += 2) {
int left = x - parity;
int right = last - x;
int y = (left >= right ? parity : last);
int w = ask(x, y);
g[x].push_back({y, w});
g[y].push_back({x, w});
}
}
vector<int> pref(n + 1, -1);
auto run = [&](int root) {
pref[root] = 0;
vector<int> st = {root};
while (!st.empty()) {
int u = st.back();
st.pop_back();
for (auto [v, w] : g[u]) {
if (pref[v] == -1) {
pref[v] = pref[u] ^ w;
st.push_back(v);
}
}
}
};
run(0);
run(1);
string ans;
ans.reserve(n);
for (int i = 1; i <= n; i++) {
ans.push_back(char('0' + (pref[i] ^ pref[i - 1])));
}
cout << '!' << ' ' << ans << endl;
int ok;
if (!(cin >> ok)) exit(0);
if (ok == -1) exit(0);
if (ok == 1) return;
for (char &c : ans) c = (c == '0' ? '1' : '0');
cout << '!' << ' ' << ans << endl;
if (!(cin >> ok)) exit(0);
if (ok == -1) exit(0);
}
int main() {
setIO();
int t;
cin >> t;
while (t--) solve();
return 0;
}#include <bits/stdc++.h>
using namespace std;
using ll = long long;
void setIO() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
}
int ask(int u, int v) {
if (u > v) swap(u, v);
int m = v - u;
cout << '?' << ' ' << u + 1 << ' ' << v << endl;
int f;
if (!(cin >> f)) exit(0);
if (f == -1) exit(0);
return (m / f) & 1;
}
void solve() {
int n;
cin >> n;
vector<vector<pair<int, int>>> g(n + 1);
for (int parity = 0; parity < 2; parity++) {
int last = n;
if ((last & 1) != parity) last--;
for (int x = parity + 2; x <= last; x += 2) {
int left = x - parity;
int right = last - x;
int y = (left >= right ? parity : last);
int w = ask(x, y);
g[x].push_back({y, w});
g[y].push_back({x, w});
}
}
vector<int> pref(n + 1, -1);
auto run = [&](int root) {
pref[root] = 0;
vector<int> st = {root};
while (!st.empty()) {
int u = st.back();
st.pop_back();
for (auto [v, w] : g[u]) {
if (pref[v] == -1) {
pref[v] = pref[u] ^ w;
st.push_back(v);
}
}
}
};
run(0);
run(1);
string ans;
ans.reserve(n);
for (int i = 1; i <= n; i++) {
ans.push_back(char('0' + (pref[i] ^ pref[i - 1])));
}
cout << '!' << ' ' << ans << endl;
int ok;
if (!(cin >> ok)) exit(0);
if (ok == -1) exit(0);
if (ok == 1) return;
for (char &c : ans) c = (c == '0' ? '1' : '0');
cout << '!' << ' ' << ans << endl;
if (!(cin >> ok)) exit(0);
if (ok == -1) exit(0);
}
int main() {
setIO();
int t;
cin >> t;
while (t--) solve();
return 0;
}