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.
View operation as choosing one edge of length . The chosen edge toggles its two endpoints, and every length can be used at most once.
For odd , split the string into and . The first part can be processed with lengths of one parity, and the second part with the other parity, so the two sweeps never fight over the same operation.
Process the left half from the center outward. Keep at most one unresolved 1 position inside the already processed area. When the new symmetric endpoints differ, use the current length to kill the old by flipping it with a future position to the right, then make the endpoint containing 1 the new .
Then process the right half from the outside inward. Again keep one active unresolved position outside the current interval. If the two endpoints differ, try to kill old using the current distance and a position strictly inside the interval; if that is impossible, freeze as one of the final leftovers.
A freeze can only happen when is far away. If was created while the interval span was and freezes when the span is , then . So freeze spans shrink by a factor of , giving at most frozen leftovers under the constraints. Add the final active position and maybe one center position: is enough. Barely, but enough.
Work with 1-indexed positions. If we flip positions , then this is operation and we output for that length.
First remove the annoying parity issue. If the original is even, we solve only the prefix of length . The operation of length is unused by that prefix. After the construction, if , flip positions . This turns into 0; the other endpoint either turns 0 into 1 or 1 into 0, so the total number of 1s does not increase. Nice little cleanup, no drama.
So assume the processed length is odd: . Since , we have .
Split the processed string into
The left half has endpoint distances , while the right half has distances . These parities are different, so every operation length is reserved for exactly one sweep.
For the left half, process from the center outward. Maintain this invariant:
In the already processed part of the left half, all positions are
0except possibly one pending position .
When adding symmetric endpoints , let .
If , they are easy. If both are 1, flip and both become 0; if both are 0, do nothing.
If , exactly one endpoint contains a 1. Before making that endpoint the new pending position, kill the old pending position if it is currently 1: flip . This is valid because lies inside the previously processed interval, so is to the right of the current pair and is still within the processed odd-length prefix. It may mess with the right half, but that is fine because the right half has not been cleaned yet. After that, the endpoint containing 1 becomes the new pending position.
After the left sweep, the whole left half contains at most one active unresolved 1.
Now process the right half from the outside inward. Maintain another invariant:
Outside the current right interval, apart from frozen leftovers we have already given up on, there is at most one active pending position .
For endpoints with :
If , handle them the same way as before: flip only when both are 1.
If , one endpoint will become the new active pending position after we shrink the interval. Before that, try to kill the old active . If , the only useful partner at distance is ; if , it is . If that partner lies strictly inside , flip the pair and old is gone. If not, old is too far away, so we freeze it as a final leftover. Then the endpoint containing 1 becomes the new active pending position.
The whole proof is in the freeze bound. Suppose an active pending position is created while the current span is . Immediately after shrinking, it is distance from the remaining interval. Later, after several inward steps, suppose it freezes when the span is and its gap from the interval is . Each inward step decreases the span by and increases the gap by , so
A freeze means the pending position is too far to pair into the interval, i.e. . Therefore
So consecutive freeze spans shrink by a factor of at least .
The first freeze span is at most . Hence there can be at most freezes, since
After the right sweep, besides frozen positions, there can be at most one active pending position and at most one unprocessed center position. Therefore the final number of 1s is at most
All unused operation lengths simply output .
The construction touches each position times, so the complexity is per test case and overall. Memory usage 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;
while (T--) {
int n;
string input;
cin >> n >> input;
int k = n / 2;
int N = (n % 2 ? n : n - 1);
int m = (N - 1) / 2;
string s = " " + input;
vector<int> ans(k + 1, 0);
auto flip = [&](int a, int b) {
if (a > b) swap(a, b);
int d = b - a;
ans[d] = a;
s[a] = (s[a] == '0' ? '1' : '0');
s[b] = (s[b] == '0' ? '1' : '0');
};
int pending = -1;
if (m % 2 == 1) {
int c = (m + 1) / 2;
pending = c;
for (int l = c - 1, r = c + 1; l >= 1; --l, ++r) {
int d = r - l;
if (s[l] == s[r]) {
if (s[l] == '1') flip(l, r);
} else {
if (pending != -1 && s[pending] == '1') flip(pending, pending + d);
pending = (s[l] == '1' ? l : r);
}
}
} else {
int l = m / 2, r = l + 1;
if (s[l] == s[r]) {
if (s[l] == '1') flip(l, r);
} else {
pending = (s[l] == '1' ? l : r);
}
for (--l, ++r; l >= 1; --l, ++r) {
int d = r - l;
if (s[l] == s[r]) {
if (s[l] == '1') flip(l, r);
} else {
if (pending != -1 && s[pending] == '1') flip(pending, pending + d);
pending = (s[l] == '1' ? l : r);
}
}
}
for (int l = m + 1, r = N; l < r; ++l, --r) {
int d = r - l;
if (s[l] == s[r]) {
if (s[l] == '1') flip(l, r);
} else {
if (pending != -1 && s[pending] == '1') {
int target = -1;
if (pending < l) {
int cand = pending + d;
if (l < cand && cand < r) target = cand;
} else if (pending > r) {
int cand = pending - d;
if (l < cand && cand < r) target = cand;
}
if (target != -1) flip(pending, target);
}
pending = (s[l] == '1' ? l : r);
}
}
if (n % 2 == 0 && s[n] == '1') {
flip(n / 2, n);
}
for (int i = 1; i <= k; ++i) {
cout << ans[i] << (i == k ? '\n' : ' ');
}
}
}#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;
string input;
cin >> n >> input;
int k = n / 2;
int N = (n % 2 ? n : n - 1);
int m = (N - 1) / 2;
string s = " " + input;
vector<int> ans(k + 1, 0);
auto flip = [&](int a, int b) {
if (a > b) swap(a, b);
int d = b - a;
ans[d] = a;
s[a] = (s[a] == '0' ? '1' : '0');
s[b] = (s[b] == '0' ? '1' : '0');
};
int pending = -1;
if (m % 2 == 1) {
int c = (m + 1) / 2;
pending = c;
for (int l = c - 1, r = c + 1; l >= 1; --l, ++r) {
int d = r - l;
if (s[l] == s[r]) {
if (s[l] == '1') flip(l, r);
} else {
if (pending != -1 && s[pending] == '1') flip(pending, pending + d);
pending = (s[l] == '1' ? l : r);
}
}
} else {
int l = m / 2, r = l + 1;
if (s[l] == s[r]) {
if (s[l] == '1') flip(l, r);
} else {
pending = (s[l] == '1' ? l : r);
}
for (--l, ++r; l >= 1; --l, ++r) {
int d = r - l;
if (s[l] == s[r]) {
if (s[l] == '1') flip(l, r);
} else {
if (pending != -1 && s[pending] == '1') flip(pending, pending + d);
pending = (s[l] == '1' ? l : r);
}
}
}
for (int l = m + 1, r = N; l < r; ++l, --r) {
int d = r - l;
if (s[l] == s[r]) {
if (s[l] == '1') flip(l, r);
} else {
if (pending != -1 && s[pending] == '1') {
int target = -1;
if (pending < l) {
int cand = pending + d;
if (l < cand && cand < r) target = cand;
} else if (pending > r) {
int cand = pending - d;
if (l < cand && cand < r) target = cand;
}
if (target != -1) flip(pending, target);
}
pending = (s[l] == '1' ? l : r);
}
}
if (n % 2 == 0 && s[n] == '1') {
flip(n / 2, n);
}
for (int i = 1; i <= k; ++i) {
cout << ans[i] << (i == k ? '\n' : ' ');
}
}
}