📅  最后修改于: 2023-12-03 15:27:33.521000             🧑  作者: Mango
细分树是一棵基于二分思想的动态数据结构,主要用于维护静态和动态集合。它可以在 $O(n)$ 的时间复杂度内预处理出一个集合的细分树,实现 $O(log^2n)$ 次操作,其中 $n$ 是集合大小。本文将介绍如何使用细分树解决给定范围的 XOR 问题。
给定一个数组 $A[1, 2, ..., n]$,每次查询区间 $[l,r]$ 的 XOR 和,即:
$$A[l] \oplus A[l+1] \oplus \cdots \oplus A[r] $$
其中 $\oplus$ 表示异或运算。
我们可以用细分树$^{[1]}$处理这个问题。我们首先将 $A$ 中的所有元素插入到一棵细分树中。每个节点都维护了一个 $m$ 维向量,表示在该节点上所有叶子节点的异或和。我们可以用下面的公式计算节点 $u$ 的向量:
$$v_u = v_{u.left} \oplus v_{u.right}$$
对于任意节点 $u$,我们可以用其向量中相应位的值计算出 $u$ 对应的叶子节点在该位上的异或和。这意味着我们可以在 $O(logn)$ 的时间内计算出 $[l,r]$ 的 XOR 和。我们只需将 $l$ 和 $r$ 分别分解成二进制数,然后从根节点开始,对于每一位,如果下一位是 $0$,则向左子树走,否则向右子树走。每经过一个节点,我们就用其向量更新答案。
为了方便计算向量,我们引入一个新的数据结构:二元数,也称为 $+1/-1$ 二元数。一个 $+1/-1$ 二元数是一个只包含 $+1$ 和 $-1$ 的有序向量,长度为 $m$。我们可以在 $O(m)$ 的时间内给出两个二元数的乘积。具体来说,令 $u$ 和 $v$ 分别表示两个二元数,我们可以按照下面的公式计算 $u \times v$:
$$[u \times v]_i = \begin{cases} u_i & \text{if $v_i = 1$} \ -u_i & \text{otherwise} \end{cases}$$
为了计算节点向量,我们可以递归地计算其子节点的向量,并用二元数的乘积计算节点向量。具体来说,设节点 $u$ 的子节点为 $u.left$ 和 $u.right$,我们可以按照下面的公式计算 $v_u$:
$$v_u = [v_{u.left} \times c_{u.left}] + [v_{u.right} \times c_{u.right}]$$
其中 $c_u$ 是一个二元数,表示节点 $u$ 的贡献。具体来说,设节点 $u$ 对应的元素为 $A_i$,则 $c_u$ 是 $+1/-1$ 二元数,其第 $i$ 位为 $+1$,其他位为 $-1$。
我们可以用下面的代码实现上述算法:
class SegmentTree:
def __init__(self, A):
# 初始化细分树
self.n = len(A)
self.tree = [None] * (4 * self.n)
self.build_tree(A, 1, 1, self.n)
def build_tree(self, A, i, l, r):
if l == r:
# 叶子节点
self.tree[i] = ([A[l-1]], [1, -1])
else:
# 中间节点
mid = (l + r) // 2
self.build_tree(A, 2*i, l, mid)
self.build_tree(A, 2*i+1, mid+1, r)
left_vals, left_coeffs = self.tree[2*i]
right_vals, right_coeffs = self.tree[2*i+1]
node_coeffs = left_coeffs + right_coeffs
node_vals = self.mult(left_vals, left_coeffs) + self.mult(right_vals, right_coeffs)
self.tree[i] = (node_vals, node_coeffs)
def query(self, l, r):
def query_node(i, cl, cr, coeffs):
vals, coeffs = self.tree[i]
if cl > r or cr < l:
# 区间不交
return ([], [])
elif cl >= l and cr <= r:
# 区间被包含
return (vals, coeffs)
else:
# 区间相交
mid = (cl + cr) // 2
left_vals, left_coeffs = query_node(2*i, cl, mid, coeffs)
right_vals, right_coeffs = query_node(2*i+1, mid+1, cr, coeffs)
return (self.mult(left_vals, left_coeffs) + self.mult(right_vals, right_coeffs), left_coeffs + right_coeffs)
_, coeffs = query_node(1, 1, self.n, [1, -1])
result = 0
for x, c in zip(coeffs, range(self.n)):
if l <= c+1 <= r:
result ^= x
return result
def mult(self, vals, coeffs):
return [x * c for x, c in zip(vals, coeffs)]
# 示例代码
A = [1, 2, 3, 4, 5]
tree = SegmentTree(A)
print(tree.query(1, 5)) # 输出 1^2^3^4^5 = 5
本文介绍了如何使用细分树解决给定范围的 XOR 问题。我们首先将 $A$ 中的所有元素插入到一棵细分树中,然后每次查询区间 $[l,r]$ 的 XOR 和时,从根节点开始遍历细分树,将经过的节点向量和答案异或起来即可。我们还介绍了二元数的概念,并给出了计算节点向量的具体方法。最后,我们给出了 Python 代码实现。