题解:CF1968F(Equal XOR Segments)
题目翻译:定义一个序列是好,当且仅当可以将其分成大于 1 1 1 份,使得每个部分的异或和相等。现在给定一个长度为 n n n 的序列 a a a,以及 q q q 次查询,每次查询中,询问序列 a a a 的第 l l l 到 r r r 位是不是好的。( n ≤ 2 ⋅ 1 0 5 n\leq2\cdot10^5 n≤2⋅105 并且 q ≤ 2 ⋅ 1 0 5 q\leq2\cdot10^5 q≤2⋅105)
第一步,要缩小范围。我们不难发现这个段数要么是 2 2 2,要么是 3 3 3,如果有多于 4 4 4 段,那么将其中任意三段合并,根据异或的性质可以证明对最终结果没有影响,这样一直合并,一直将段数减少 2 2 2,早晚能变成 2 2 2 或 3 3 3 段。
第二步,考虑如何求答案。如果询问道一段区间异或起来(用前缀异或和
s
s
s 维护)为
0
0
0,那么一定可以分成两段;否则也就是难点——分成三段的情况。那么就相当于找到两个数
x
x
x 和
y
y
y(
l
<
x
<
y
<
r
l<x<y<r
l<x<y<r)使得
s
x
⨁
s
l
−
1
=
s
y
⨁
s
x
=
s
r
⨁
s
y
s_x\bigoplus s_{l-1}=s_y\bigoplus s_x=s_r\bigoplus s_y
sx⨁sl−1=sy⨁sx=sr⨁sy。这个式子就等价于找到
s
y
=
s
l
−
1
s_y=s_{l-1}
sy=sl−1 以及
s
x
=
s
r
s_x=s_r
sx=sr。那么我们用一个 map<int, vector<int>>
存储某一个数在
s
s
s 中出现在哪些位置,然后用 upper_bound
和 lower_bound
二分求出能否找到合理的
x
x
x 和
y
y
y 并且保证
x
<
y
x<y
x<y。比如说,我们在等于
s
l
−
1
s_{l-1}
sl−1 的 vector
里面找到小于
r
r
r 最大的
y
y
y,在等于
s
r
s_r
sr 的 vector
里面找到大于
l
l
l 里最小的
x
x
x,判断,如果
l
<
x
<
y
<
r
l<x<y<r
l<x<y<r 就可以,否则就不行。
具体见代码。
#include <bits/stdc++.h>
#define N 220000
using namespace std;
int t, n, q, a[N], l, r;
int s[N];
map<int, set<int>> id;
int main() {
scanf("%d", &t);
while (t--) {
id.clear();
scanf("%d%d", &n, &q);
for (int i = 1; i <= n; i++) {
scanf("%d", &a[i]);
s[i] = s[i - 1] ^ a[i];
if (id.find(s[i]) == id.end()) {
id[s[i]] = {i};
} else {
id[s[i]].insert(i);
}
}
for (int i = 1; i <= q; i++) {
scanf("%d%d", &l, &r);
if ((s[r] ^ s[l - 1]) == 0) {
printf("Yes\n");
} else {
auto u = id[s[l - 1]].upper_bound(r - 1);
if (u == id[s[l - 1]].begin()) {
printf("No\n");
} else {
u--;
auto v = id[s[r]].lower_bound(l);
if (v != id[s[r]].end() && l <= *v && *v < *u && *u < r) {
printf("Yes\n");
} else {
printf("No\n");
}
}
}
}
}
return 0;
}