C o d e f o r c e s R o u n d 943 ( D i v . 3 ) F . E q u a l X O R S e g m e n t s \Huge{Codeforces~Round~943~(Div.3)F.Equal~XOR~Segments} Codeforces Round 943 (Div.3)F.Equal XOR Segments
题意
给出一个长度为 n n n的数组,然后有 m m m次询问,每次询问区间 [ l , r ] [l,r] [l,r]是否是有趣的。
- 有趣的定义: 将区间分为 k k k段 ( 1 < k ) (1<k) (1<k),使得每段的异或值相等。
思路
区间异或值我们可以通过预处理来解决, b i = b i − 1 ⊕ a i , a l ⊕ a l + 1 . . . a r = b r ⊕ b l − 1 b_i=b_{i-1}⊕a_i~,~a_{l}⊕a_{l+1}...a_r=b_r⊕b_{l-1} bi=bi−1⊕ai , al⊕al+1...ar=br⊕bl−1。
重点就是如何判断区间是否可以分段。
如果区间有趣,那么分成的k段异或值相等。
{
x
⊕
x
=
0
x
⊕
0
=
x
⇒
x
⊕
x
⊕
x
⊕
=
x
\left\{\begin{array}{l} x \oplus x=0 \\ x \oplus 0=x \end{array} \Rightarrow x \oplus x \oplus x \oplus=x\right.
{x⊕x=0x⊕0=x⇒x⊕x⊕x⊕=x
- 若k为偶数,那么我们可以按照上面异或的性质合并,那么会发现区间异或值为0。
- 若k为奇数,那么我们可以将k段合并为三段:
0
x
0
0~x~0
0 x 0的形式,即一三段为
0
0
0,第二段可以为任意值。
- 容易证明,当为奇数时,只有这种合并方式可以逆推出区间是否有趣。
标程
void Solved() {
int n, m; cin >> n >> m;
vector<int> v(n + 1);
map<int, vector<int>> mp;
mp[0].push_back(0);
for(int i = 1; i <= n; i ++ ) {
int x; cin >> x;
v[i] = x ^ v[i - 1]; //算一下异或前缀和
mp[v[i]].push_back(i); //将可以得到这个结果的下标放一起
}
while(m -- ) {
int l, r; cin >> l >> r;
if(v[l - 1] == v[r]) { //k为偶数或k为奇数且x=0
cout << "YES\n"; continue;
}
auto &it1 = mp[v[l - 1]];
int p1 = *--lower_bound(it1.begin(), it1.end(), r); //找小于等于r的第一个数
auto &it2 = mp[v[r]];
int p2 = *lower_bound(it2.begin(), it2.end(), l); //找大于等于l的第一个数
cout << (p1 > p2 ? "YES" : "NO") << '\n';
}
cout << endl;
}