📜  二维二元索引树或Fenwick树(1)

📅  最后修改于: 2023-12-03 15:21:40.462000             🧑  作者: Mango

二维二元索引树(Fenwick Tree)

简介

二维二元索引树,又称二维树状数组或Fenwick树,是树状数组的一种扩展应用,用于处理二维前缀和问题,其时间和空间复杂度均为 $O(n \log n)$。

定义

树状数组是一种快速计算前缀和的数据结构,它可以支持单点修改和区间查询,并且时间复杂度均为 $O(\log n)$。

对于一维数组,树状数组的下标顺序从小到大,每个元素都存储一个前缀和,某个下标处的元素的值表示该位置之前的所有元素之和。

而对于二维数组,我们可以使用两个下标构建树状数组,使其支持二维前缀和操作。

原理

首先,我们需要对目标二维数组 $A$ 进行前缀和处理。具体来说,我们先将第一维下标按顺序排序,再在每一行中计算前缀和。这样,原本的二维数组 $A$ 就被转化成了一个类似于平面坐标系的数据结构 $P$,其中 $P_{i,j}$ 表示第 $i$ 行(按第一维下标排序后)第 $j$ 列之前所有元素之和。

接下来,我们使用两个一维树状数组 $T_1$ 和 $T_2$,其中 $T_1$ 负责处理第一维下标,$T_2$ 负责处理第二维下标。$T_1$ 和 $T_2$ 的长度均为 $n$,表示从 $1$ 到 $n$ 的下标范围。

树状数组 $T_1$ 和 $T_2$ 的元素值表示二维平面中某个矩形的前缀和(包含左上角和右下角)。具体来说,下标为 $(i,j)$ 的 $T_1$ 元素表示第 $i$ 行前 $j$ 个位置的元素之和,下标为 $(i,j)$ 的 $T_2$ 元素表示前 $i$ 行第 $j$ 列之前所有元素之和。

那么对于一个二维前缀和查询,我们可以使用以下步骤:

  1. 对于每一行 $i$,通过二分查找 $T_1$ 中下标为 $(i,j)$ 的元素,找到使其最大的 $j$,满足 $j \leq column$。这里 $column$ 表示查询的右边界。
  2. 对于每一列 $j$,也通过二分查找 $T_2$ 中下标为 $(i,j)$ 的元素,找到使其最大的 $i$,满足 $i \leq row$。这里 $row$ 表示查询的下边界。
  3. 计算矩阵的前缀和。设 $(x,y)$ 表示能够被上述步骤 1 和 2 找到的最大下标,那么矩阵的前缀和等于 $P_{x,y} - P_{x,row-1} - P_{column-1,y} + P_{row-1,column-1}$。

在查询的过程中,我们使用两个二分查找操作,时间复杂度为 $O(\log^2 n)$,因此整个查询的时间复杂度为 $O(\log^2 n)$。

代码实现

二维二元索引树主要包含以下两个操作:

  1. 单点修改
  2. 区间查询

以 Python 为例,代码实现如下:

class FenwickTree2D:
    def __init__(self, n):
        self.n = n
        self.tree = [[0] * (n + 1) for _ in range(n + 1)]
    
    def update(self, x, y, v):
        i, j = x, y
        while i <= self.n:
            j = y
            while j <= self.n:
                self.tree[i][j] += v
                j += (j & -j)
            i += (i & -i)
    
    def sum(self, x, y):
        res = 0
        i, j = x, y
        while i > 0:
            j = y
            while j > 0:
                res += self.tree[i][j]
                j -= (j & -j)
            i -= (i & -i)
        return res

这里我们使用了一个二维列表 tree 存储树状数组的元素。update 操作用于单点修改,将矩阵上的某个点增加 $v$。sum 操作用于区间查询,计算矩形 $(1,1,x,y)$ 中所有元素之和。