Loading

题解 [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\) 次操作:

  1. 给定 \(x,v\),将 \(a_x\) 异或 \(v\)
  2. 给定 \(x\),在 \(S\) 中加入 \(x\)
  3. 给定 \(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\) 放到线性基上查询,得到的结果就是

\[x\operatorname{xor} \bigoplus_{i\not\in x\land 0\le i<64} b_i. \]

这样就可以拆位考虑了。假如 \(b_j>0\),那么异或后的结果的第 \(j\) 位一定是 \(1\);假如 \(b_j=0\),则一个数 \(a_i\) 异或后的结果的第 \(j\) 位是 \(1\),当且仅当

\[[j\in a_i]+\sum_{0\le k<64} [j\in b_k\land k\not\in a_i] \]

是奇数。

于是,假如我们对每一位都建一棵线段树来维护一个 \(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;
}
posted @ 2023-11-07 21:48  Alan_Zhao_2007  阅读(0)  评论(0)    收藏  举报