📜  二维差分阵列(1)

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

二维差分阵列

简介

二维差分阵列可以用于快速计算矩阵区间的和,支持单点修改与区间查询操作。在一些图形学、计算几何以及动态规划问题中经常被使用。

实现

二维差分阵列的实现原理基于前缀和的思想。假设有一个二维矩阵 matrix,则在二维差分阵列中,我们要维护一个二维的数组 diff,满足:

  • diff[i][j] = matrix[i][j] - matrix[i-1][j] - matrix[i][j-1] + matrix[i-1][j-1],其中 0 <= i < matrix.length0 <= j < matrix[0].length
  • diff[0][0] = matrix[0][0]

此时若想查询矩阵中以 x1 为左上角、以 x2 为右下角的矩阵区间之和,则可以使用如下公式:

sum(x1, y1, x2, y2) = diff[x2][y2] - diff[x1-1][y2] - diff[x2][y1-1] + diff[x1-1][y1-1]

根据容斥原理,可以把矩阵区间中重复计算的部分用差分数组的四个元素加减消掉。

为了方便单点修改操作,我们需要对差分数组进行类似前缀和的预处理操作。假设有一个操作 (x, y, val),表示将矩阵中 (x, y) 位置的元素加上 val,则可以使用如下公式进行单点修改操作:

matrix[x][y] += val;
for (int i = x; i < matrix.length; i += i & -i) {
    for (int j = y; j < matrix[0].length; j += j & -j) {
        diff[i][j] += val - (i == x ? 0 : matrix[i-1][j]-matrix[i-1][j-1]) - (j == y ? 0 : matrix[i][j-1]-matrix[i-1][j-1]);
    }
}
时间复杂度
  • 初始化:$O(nm)$。
  • 单点修改:$O(\log n\log m)$。
  • 区间查询:$O(\log n\log m)$。
代码实现
public class BinaryIndexedTree2D {
    private int[][] matrix;
    private int[][] diff;

    public BinaryIndexedTree2D(int[][] matrix) {
        this.matrix = matrix;
        int n = matrix.length, m = matrix[0].length;
        diff = new int[n+1][m+1];
        for (int i = 1; i <= n; i++) {
            for (int j = 1; j <= m; j++) {
                diff[i][j] = matrix[i-1][j-1] - (i > 1 ? matrix[i-2][j-1] : 0)
                           - (j > 1 ? matrix[i-1][j-2] : 0) + (i > 1 && j > 1 ? matrix[i-2][j-2] : 0);
                for (int ii = i-(i&-i); ii < i; ii++) {
                    for (int jj = j-(j&-j); jj < j; jj++) {
                        diff[i][j] -= (ii > 0 && jj > 0 ? diff[ii][jj] : 0);
                    }
                }
            }
        }
    }

    public void add(int x, int y, int val) {
        matrix[x][y] += val;
        for (int i = x+1; i < diff.length; i += i & -i) {
            for (int j = y+1; j < diff[0].length; j += j & -j) {
                diff[i][j] += val;
                if (x > 0) diff[i][j] -= matrix[x-1][y];
                if (y > 0) diff[i][j] -= matrix[x][y-1];
                if (x > 0 && y > 0) diff[i][j] += matrix[x-1][y-1];
            }
        }
    }

    public int sum(int x1, int y1, int x2, int y2) {
        return query(x2, y2) - query(x1-1, y2) - query(x2, y1-1) + query(x1-1, y1-1);
    }

    private int query(int x, int y) {
        int res = 0;
        for (int i = x+1; i > 0; i -= i & -i) {
            for (int j = y+1; j > 0; j -= j & -j) {
                res += diff[i][j];
            }
        }
        return res;
    }
}
参考资料