题解 [IOI 2024 集训队互测 Day5] Xor Master
题意
给定长为 \(n\) 的非负整数序列 \(a_1,a_2,\dots,a_n\),以及一个数集 \(S\),初始 \(S\) 为空。
对于集合 \(T\),定义 \(h(T)\) 为 \(T\) 中所有元素的异或和。
定义 \(f(l,r)\) 为:设 \(x=\operatorname{xor}_{i=l}^r a_i\),令 \(f(l,r)=\max_{T\subseteq S} \{x\operatorname{xor} h(T)\}\)。
有 \(q\) 次操作:
- 给定 \(x,v\),将 \(a_x\) 异或 \(v\);
- 给定 \(x\),在 \(S\) 中加入 \(x\);
- 给定 \(l,r\),求 \(\sum_{i=l}^r f(l,i)\)。
\(n\le 5\times 10^5\),\(q\le 10^5\),所有输入的数都小于 \(2^{64}\)。时限两秒。
题解
首先我们把 \(a_i\) 变成 \(\operatorname{xor}_{j=1}^i a_j\)。我们要支持的是:
- 在线性基中插入一个数;
- 将 \(a_i\) 的某个区间异或上一个数;
- 求一段区间的 \(a_i\) 在线性基上查询最大值得到的结果之和。
我们用高斯消元法得到线性基,设线性基是 \(b_0,b_1,\dots,b_{63}\),这样有一个性质:
假如 \(b_i>0\),那么线性基上其他位置的数的第 \(i\) 位都一定是 \(0\)。
所以,将一个数 \(x\) 放到线性基上查询,得到的结果就是
这样就可以拆位考虑了。假如 \(b_j>0\),那么异或后的结果的第 \(j\) 位一定是 \(1\);假如 \(b_j=0\),则一个数 \(a_i\) 异或后的结果的第 \(j\) 位是 \(1\),当且仅当
是奇数。
于是,假如我们对每一位都建一棵线段树来维护一个 \(01\) 序列,需要支持的就是
- 区间 \(0\) 变成 \(1\),\(1\) 变成 \(0\);
- 求区间和。
此外,当线性基中新插入了一个数,所有的线段树都应该重构。这样能做到 \(O(n\log^2 V+q\log n\log V)\)。
然后先考虑,假如线性基中多了一个数,那么 \(a_i\) 的最大异或和会怎么变化。高斯消元法求线性基的过程只会将若干个位置的 \(b_j\) 异或上某个 \(x\),假如异或的位置的集合是 \(S\),那么去除掉一定是 \(1\) 的那一位以后,\(|S\cap \overline{a_i}|\) 需要是奇数,最大异或和才会变化,变化的方式就是异或上了 \(x\)。
我们考虑将所有线段树合在一起维护,这样每个线段树节点上是 \(64\) 个 \([0,L]\) 间的整数,其中 \(L\) 是这个节点的大小。我们将这个整数组成的方阵转置,维护 \(\lfloor\log L\rfloor+1\) 个 \([0,2^{64})\) 间的数。考虑线段树过程中需要支持什么:
- 信息和信息合并:并行的二进制加法;
- 标记和信息合并:并行的二进制减法;
- 线性基内新插入了一个数:显然可以 \(O(n)\) 重构线段树。
这样,重构的时间复杂度是 \(T(n)=2T(\frac{n}{2})+O(\log n)\),解得 \(T(n)=O(n)\)。总的时间复杂度就是 \(O(n\log V+q\log n\log V)\)。
代码
(主要用来记录一下高斯消元求线性基的简单写法)
#include <bits/stdc++.h>
using namespace std;
#define For(Ti,Ta,Tb) for(auto Ti=(Ta);Ti<=(Tb);++Ti)
#define Dec(Ti,Ta,Tb) for(auto Ti=(Ta);Ti>=(Tb);--Ti)
#define debug(...) fprintf(stderr,__VA_ARGS__)
#define range(Tx) begin(Tx),end(Tx)
using ll = long long;
using ull = unsigned long long;
const int N = 5e5 + 5;
int n, q;
ull a[N], tag[N], val[64];
struct Basis {
ull v[64];
pair<ull, ull> insert(ull x) {
Dec(i, 63, 0) {
if (x >> i & 1) {
if (!v[i]) {
v[i] = x;
Dec(j, i - 1, 0) {
if (v[i] >> j & 1) {
v[i] ^= v[j];
}
}
ull chg = 1LLU << i;
For(j, i + 1, 63) {
if (v[j] >> i & 1) {
v[j] ^= v[i];
chg |= 1LLU << j;
}
}
return { v[i],chg };
}
x ^= v[i];
}
}
return { 0,0 };
}
}base;
struct Segment_Tree {
struct Node {
int len, log;
ull tag, v[20];
}t[N * 4];
void apply(int p, ull v) {
ull c = 0;
For(i, 0, t[p].log) {
ull cur = (t[p].len >> i & 1 ? v : 0), val = t[p].v[i];
t[p].v[i] = (val & ~v) | (cur ^ (val & v) ^ c);
c = v & ((~cur & (val | c)) | (val & c));
}
t[p].tag ^= v;
}
void push(int p) {
if (t[p].tag) {
apply(p * 2, t[p].tag);
apply(p * 2 + 1, t[p].tag);
t[p].tag = 0;
}
}
void pull(int p) {
const auto& vl = t[p * 2].v;
const auto& vr = t[p * 2 + 1].v;
ull c = 0;
For(i, 0, t[p].log) {
t[p].v[i] = vl[i] ^ vr[i] ^ c;
c = (c & (vl[i] | vr[i])) | (vl[i] & vr[i]);
}
}
void init(ull x, ull chg, int b, int p = 1, int L = 1, int R = n) {
t[p].len = R - L + 1;
t[p].log = __lg(t[p].len);
if (L == R) {
if (!x) {
t[p].v[0] = a[L];
} else if (__builtin_parityll(~a[L] & chg)) {
t[p].v[0] ^= x;
}
if (x) {
t[p].v[0] ^= t[p].v[0] & (1LLU << b);
}
return;
}
push(p);
int mid = (L + R) / 2;
init(x, chg, b, p * 2, L, mid);
init(x, chg, b, p * 2 + 1, mid + 1, R);
pull(p);
}
void range_xor(int l, int r, ull v, int p = 1, int L = 1, int R = n) {
if (l <= L && R <= r) {
apply(p, v);
return;
}
push(p);
int mid = (L + R) / 2;
if (l <= mid) range_xor(l, r, v, p * 2, L, mid);
if (r > mid) range_xor(l, r, v, p * 2 + 1, mid + 1, R);
pull(p);
}
ull query(int l, int r, ull x, int p = 1, int L = 1, int R = n) {
if (l > R || r < L) {
return 0;
}
if (l <= L && R <= r) {
ull res = 0, c = 0;
For(i, 0, t[p].log) {
ull cur = (t[p].len >> i & 1 ? x : 0), val = t[p].v[i];
res += ((val & ~x) | (cur ^ (val & x) ^ c)) * (1 << i);
c = x & ((~cur & (val | c)) | (val & c));
}
return res;
}
push(p);
int mid = (L + R) / 2;
return query(l, r, x, p * 2, L, mid) + query(l, r, x, p * 2 + 1, mid + 1, R);
}
}seg;
void rebuild(int b) {
val[b] = 0;
For(i, 0, 63) {
if (base.v[i] >> b & 1) {
val[b] |= 1LLU << i;
}
}
}
ull get(ull x) {
ull v = 0;
For(i, 0, 63) {
if (!base.v[i] && __builtin_parityll((x & val[i]) ^ (x >> i & 1))) {
v ^= 1LLU << i;
}
}
return v;
}
void range_xor(int l, int r, ull x) {
tag[l] ^= x;
tag[r + 1] ^= x;
ull v = get(x);
seg.range_xor(l, r, v);
}
struct BIT {
ull t[N];
void add(int p, ull x) {
for (; p <= n; p += p & -p) t[p] ^= x;
}
ull query(int p) {
ull res = 0;
for (; p; p &= p - 1) res ^= t[p];
return res;
}
}bit;
int main() {
cin.tie(nullptr)->sync_with_stdio(false);
cin >> n >> q;
For(i, 1, n) {
cin >> a[i];
bit.add(i, a[i]);
a[i] ^= a[i - 1];
}
For(i, 0, 63) rebuild(i);
seg.init(0, 0, 0);
For(test, 1, q) {
int tp;
cin >> tp;
if (tp == 1) {
int x;
ull v;
cin >> x >> v;
bit.add(x, v);
range_xor(x, n, v);
} else if (tp == 2) {
ull x;
cin >> x;
auto [res, chg] = base.insert(x);
if (res) {
For(i, 1, n) {
tag[i] ^= tag[i - 1];
a[i] ^= tag[i];
}
For(i, 1, n) tag[i] = 0;
For(i, 0, 63) {
if (!base.v[i]) {
rebuild(i);
}
}
seg.init(res, chg, __lg(res));
}
} else {
int l, r;
cin >> l >> r;
ull ans = seg.query(l, r, get(bit.query(l - 1)));
For(i, 0, 63) {
if (base.v[i]) {
ans += (1LLU << i) * (r - l + 1);
}
}
cout << ans << '\n';
}
}
return 0;
}

浙公网安备 33010602011771号