题解 | #Kuriyama Mirai and Exclusive Or#
Guess and lies
https://ac.nowcoder.com/acm/contest/11254/A
I - Kuriyama Mirai and Exclusive Or
题意:
序列上两种操作,对异或上
,对
异或上
,问最后序列的样子。
思路:
区间修改,只问最后长啥样,试试看异或差分。操作直接搞就行,操作
显得很复杂,看起来只能单点修改。观察
这个式子。当
时,这个式子等价于
,也等价于
,看看能否把这个异或的式子加入到异或差分中。那么首先对于
这个区间,我们先对其整体异或上
,然后问题就转换成了对
这个区间异或上
。首先肯定不能一个个推过去,这样和前面暴力是没有区别的。设
,把异或的值都拿出来,得到
。我们以
为中间的边界,可以把右边的值写成
。相当于从原本要异或的值中取出前一半,复制一份给后一半,同时将后一半的区间整体异或上
。对于每个
来说,我都能递归到
,所以第二步就转换成了对区间的异或修改。这里每一次都递归会显得很多余,我们可以先打个标记,最后从大到小一起转移就行。
每一步完成这样的操作后,注意更新和
,把他们都加上
。最后一步可能会超出我需要修改的右区间
,这里我们只需要从大到小枚举
,尝试用
去更新就行了。因为
更新不了,后面填上去的数字在
的二进制位上一定在最后一位
后面,即一定为
,所以也能转换成异或操作。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int inf = 0x3f3f3f3f;
const ll INF = 0x3f3f3f3f3f3f3f3f;
const int N = 6e5 + 7;
bool tag[N][30];
int a[N];
void solve() {
int n, q;
cin >> n >> q;
for (int i = 0; i < n; ++i) {
cin >> a[i];
if (i) a[i] ^= a[i - 1];
}
while (q--) {
int op, l, r, x;
cin >> op >> l >> r >> x;
l--, r--;
if (!op) {
a[l] ^= x;
a[r + 1] ^= x;
continue;
}
for (int i = x & -x; l + i <= r + 1; x += i, l += i, i = x & -x) {
a[l] ^= x;
a[l + i] ^= x;
tag[l][__lg(i)] ^= true;
}
for (int i = 29; i >= 0; --i) {
int k = 1 << i;
if (l + k <= r + 1) {
a[l] ^= x;
a[l + k] ^= x;
tag[l][i] ^= true;
l += k;
x += k;
}
}
}
for (int j = 29; j >= 1; --j) {
for (int i = 0; i < n; ++i) {
if (!tag[i][j]) continue;
tag[i][j - 1] ^= true;
int k = 1 << (j - 1);
if (i + k <= n + 1) {
tag[i + k][j - 1] ^= true;
a[i + k] ^= k;
}
if (i + k * 2 <= n + 1) a[i + k * 2] ^= k;
}
}
for (int i = 0; i < n; ++i) {
if (i) a[i] ^= a[i - 1];
cout << a[i] << ' ';
}
}
int main() {
#ifndef LOCAL
ios::sync_with_stdio(false);
cin.tie(nullptr);cout.tie(nullptr);
cout << fixed << setprecision(20);
#endif
int t = 1;
while (t--) solve();
return 0;
} 
