题解:P11872 [威海市赛 2024] 异或盒子 1
Loyal_Soldier · · 题解
有一个长度为
n 的序列a ,有以下3 种操作:
- 将一个
a_x 修改为y 。- 全局
+1 。- 询问全局异或和。
思路
考虑 01 Trie。
但是,如果和普通 01 Trie 一样,从高位往低位建树,一次会修改很多节点,并且这题不需要查询前缀,于是反过来,考虑从低位往高位建树。
考虑维护一个
考虑全局
插入和正常 01 Trie 一样。
代码
#include <bits/stdc++.h>
using namespace std;
const int MAXN = 1e6 + 10;
const int MAXM = 4e7 + 10;
const int MAXH = 30;
int n, m, a[MAXN];
vector <int> G[MAXN];
int ch[MAXM][2], w[MAXM], xor_sum[MAXM], cnt, lzy;
//ch[u][0/1] 表示 0/1 方向儿子,lzy 是全局标记
int root;
int fa[MAXN];
void pushup(int u) {
//w 奇偶性,xor_sum 就是子树异或和
w[u] = xor_sum[u] = 0;
if (ch[u][0]) {
w[u] ^= w[ch[u][0]];
xor_sum[u] ^= (xor_sum[ch[u][0]] << 1);
}
if (ch[u][1]) {
w[u] ^= w[ch[u][1]];
//偶数的话这一位就抵消了
xor_sum[u] ^= (xor_sum[ch[u][1]] << 1) | (w[ch[u][1]] & 1);
}
}
void add(int u) {
swap(ch[u][0], ch[u][1]);
if (ch[u][0]) add(ch[u][0]);
pushup(u);
}
void insert(int &u, int x, int dep) {
if (!u) u = ++cnt;
if (dep > MAXH) {
//写 ^ 1 可以让两次插入抵消变成删除,就不用再写删除了
w[u] ^= 1;
return;
}
insert(ch[u][x & 1], x >> 1, dep + 1);
pushup(u);
}
signed main() {
ios::sync_with_stdio(0);
cin.tie(0), cout.tie(0);
cin >> n >> m;
for (int i = 1; i <= n; i++) cin >> a[i], insert(root, a[i], 0);
while (m--) {
int op, x, y;
cin >> op;
if (op == 0) {
cin >> x >> y;
insert(root, a[x] + lzy, 0);
//y - lzy + lzy = y
a[x] = y - lzy;
insert(root, a[x] + lzy, 0);
} else if (op == 1) lzy++, add(root);
else cout << xor_sum[root] << '\n';
}
return 0;
}