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.
Ignore the spooky operation wording for a second. For a fixed query , position can only ever use values from positions through , so its final value must belong to the XOR-span of .
That condition is also sufficient. Build the final array from right to left: when setting position , all earlier original values are still untouched, so you can zero/keep and XOR in exactly the earlier originals you need.
Now the query becomes: can we pick where each belongs to the current prefix XOR-span? Greedily pick the smallest possible greater than the previous value. Picking larger is just making your life worse for no prize.
For one fixed left endpoint , the prefix span only changes when a new value is linearly independent. Since values are below , this happens at most 20 times. Between two such change points, the allowed set is fixed.
Use a XOR basis with order statistics: count how many span values are , and find the -th smallest span value. Then for every , simulate only its at-most-20 basis change points and compute the farthest valid right endpoint .
The core move is to stop thinking about the operations procedurally and ask what each position can become.
For a query , suppose the final sequence is
.
Position can only be affected by operations using source indices . Also, values at those source indices themselves only come from even earlier original values. So no value from the right can ever leak left. Therefore
must belong to the XOR-span of .
This is necessary. It is also sufficient, which is the part that makes the problem click.
Build the final values from right to left. When setting position , positions are still untouched originals, because we have not processed them yet. If the desired linear combination uses , leave it; otherwise zero position with the operation . Then XOR in exactly the earlier original values needed for the combination. Later changes to those earlier positions do not affect position anymore.
So the query is equivalent to this cleaner problem:
Can we choose a strictly increasing sequence such that belongs to , where over GF(2)?
Greedy
At position , say the previous chosen value is . The best choice is the smallest value in that is strictly greater than .
If some valid solution chooses a larger value, replacing it with the smaller valid one cannot hurt any future position. It only lowers the bar for the rest of the sequence. So the greedy choice is forced, not just a cute guess.
Now we need fast operations on a XOR-span:
With these, if the current span is and current value is , then the value after taking consecutive greedy choices from the same fixed span is
.
If that index is outside the span size, the span ran out of usable values.
XOR basis order statistics
Store a standard XOR basis, one vector per highest set bit. Walk bits from high to low.
For , every pivot bit splits the remaining span values into two equal halves: values with this bit 0 and values with this bit 1. To produce sorted order, decide which half contains the -th value, and XOR the basis vector if needed to force the current bit.
For , again walk high to low. Whenever has bit 1 at a controllable pivot bit, all values with a smaller prefix at that bit contribute half of the remaining combinations. If a bit is not controllable, then the current forced prefix either already became smaller/larger than , so the answer is decided immediately.
Then .
Only change points matter
Fix a left endpoint . The spans
only change when the next array value is linearly independent of the current span. Since numbers are below , the rank is at most 20, so there are at most 20 change points.
Between two change points, the allowed span is constant. Suppose at a change point index the greedy value becomes , and the current rank is . The span has exactly values. The number of additional positions we can fill before this same span runs out is
.
So for each , we simulate only those few change points and compute , the farthest right endpoint that still works.
At the next change point :
Maintaining change points while moving left
Process from right to left. Keep the change points for the current starting index.
When adding :
After all are computed, each query is just:
is YES iff .
That is the whole trick: prefix-span reachability, greedy increasing choices, and only 20 meaningful span changes per left endpoint. The problem looks cursed until those three pieces line up; then it is just a sharp XOR-basis implementation.
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
void setIO() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
}
const int LG = 21;
struct XorBasis {
int basis[LG]{};
vector<int> who;
int rank = 0;
void reset() {
fill(basis, basis + LG, 0);
who.clear();
rank = 0;
}
bool add(int x, int idx) {
for (int b = LG - 1; b >= 0; --b) {
if (((x >> b) & 1) == 0) continue;
if (!basis[b]) {
basis[b] = x;
who.push_back(idx);
++rank;
return true;
}
x ^= basis[b];
}
return false;
}
int kth(int k) const {
if (k < 1 || k > (1 << rank)) return -1;
int x = 0;
int cnt = 1 << rank;
for (int b = LG - 1; b >= 0; --b) {
if (!basis[b]) continue;
cnt >>= 1;
if (k > cnt) {
if (((x >> b) & 1) == 0) x ^= basis[b];
k -= cnt;
} else {
if ((x >> b) & 1) x ^= basis[b];
}
}
return x;
}
int countLess(int x) const {
if (x <= 0) return 0;
int ans = 0;
int cnt = 1 << rank;
int mask = 0;
for (int b = LG - 1; b >= 0; --b) {
if (basis[b]) {
cnt >>= 1;
if ((x >> b) & 1) {
ans += cnt;
if (((mask >> b) & 1) == 0) mask ^= basis[b];
} else {
if ((mask >> b) & 1) mask ^= basis[b];
}
} else {
if (((x ^ mask) >> b) & 1) {
return ((x >> b) & 1) ? ans + cnt : ans;
}
}
}
return ans;
}
int countLeq(int x) const {
return countLess(x + 1);
}
int jumpK(int value, int k) const {
return kth(countLeq(value) + k);
}
};
int main() {
setIO();
int tc;
cin >> tc;
while (tc--) {
int n, q;
cin >> n >> q;
vector<int> a(n);
for (int &x : a) cin >> x;
vector<int> maxR(n);
XorBasis cur;
cur.reset();
for (int l = n - 1; l >= 0; --l) {
if (!cur.add(a[l], l)) {
vector<int> ids = cur.who;
ids.push_back(l);
sort(ids.begin(), ids.end());
cur.reset();
for (int idx : ids) cur.add(a[idx], idx);
}
sort(cur.who.begin(), cur.who.end());
XorBasis changes;
changes.reset();
swap(cur, changes);
int bestR = n - 1;
int maxExtend = l;
int prevIndex = l - 1;
int prevValue = -1;
for (int idx : changes.who) {
if (idx - 1 > maxExtend) {
bestR = min(bestR, maxExtend);
break;
}
int before = (prevValue == -1 ? -1 : cur.jumpK(prevValue, idx - 1 - prevIndex));
cur.add(a[idx], idx);
int here = cur.jumpK(before, 1);
if (here == -1) {
bestR = min(bestR, idx - 1);
break;
}
maxExtend = idx + (1 << cur.rank) - cur.countLeq(here);
prevIndex = idx;
prevValue = here;
}
bestR = min(bestR, maxExtend);
maxR[l] = bestR;
swap(cur, changes);
}
while (q--) {
int l, r;
cin >> l >> r;
--l;
--r;
cout << (r <= maxR[l] ? "YES" : "NO") << '\n';
}
}
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 LG = 21;
struct XorBasis {
int basis[LG]{};
vector<int> who;
int rank = 0;
void reset() {
fill(basis, basis + LG, 0);
who.clear();
rank = 0;
}
bool add(int x, int idx) {
for (int b = LG - 1; b >= 0; --b) {
if (((x >> b) & 1) == 0) continue;
if (!basis[b]) {
basis[b] = x;
who.push_back(idx);
++rank;
return true;
}
x ^= basis[b];
}
return false;
}
int kth(int k) const {
if (k < 1 || k > (1 << rank)) return -1;
int x = 0;
int cnt = 1 << rank;
for (int b = LG - 1; b >= 0; --b) {
if (!basis[b]) continue;
cnt >>= 1;
if (k > cnt) {
if (((x >> b) & 1) == 0) x ^= basis[b];
k -= cnt;
} else {
if ((x >> b) & 1) x ^= basis[b];
}
}
return x;
}
int countLess(int x) const {
if (x <= 0) return 0;
int ans = 0;
int cnt = 1 << rank;
int mask = 0;
for (int b = LG - 1; b >= 0; --b) {
if (basis[b]) {
cnt >>= 1;
if ((x >> b) & 1) {
ans += cnt;
if (((mask >> b) & 1) == 0) mask ^= basis[b];
} else {
if ((mask >> b) & 1) mask ^= basis[b];
}
} else {
if (((x ^ mask) >> b) & 1) {
return ((x >> b) & 1) ? ans + cnt : ans;
}
}
}
return ans;
}
int countLeq(int x) const {
return countLess(x + 1);
}
int jumpK(int value, int k) const {
return kth(countLeq(value) + k);
}
};
int main() {
setIO();
int tc;
cin >> tc;
while (tc--) {
int n, q;
cin >> n >> q;
vector<int> a(n);
for (int &x : a) cin >> x;
vector<int> maxR(n);
XorBasis cur;
cur.reset();
for (int l = n - 1; l >= 0; --l) {
if (!cur.add(a[l], l)) {
vector<int> ids = cur.who;
ids.push_back(l);
sort(ids.begin(), ids.end());
cur.reset();
for (int idx : ids) cur.add(a[idx], idx);
}
sort(cur.who.begin(), cur.who.end());
XorBasis changes;
changes.reset();
swap(cur, changes);
int bestR = n - 1;
int maxExtend = l;
int prevIndex = l - 1;
int prevValue = -1;
for (int idx : changes.who) {
if (idx - 1 > maxExtend) {
bestR = min(bestR, maxExtend);
break;
}
int before = (prevValue == -1 ? -1 : cur.jumpK(prevValue, idx - 1 - prevIndex));
cur.add(a[idx], idx);
int here = cur.jumpK(before, 1);
if (here == -1) {
bestR = min(bestR, idx - 1);
break;
}
maxExtend = idx + (1 << cur.rank) - cur.countLeq(here);
prevIndex = idx;
prevValue = here;
}
bestR = min(bestR, maxExtend);
maxR[l] = bestR;
swap(cur, changes);
}
while (q--) {
int l, r;
cin >> l >> r;
--l;
--r;
cout << (r <= maxR[l] ? "YES" : "NO") << '\n';
}
}
return 0;
}