区间修改矩阵与查询(树状数组)

这篇博客介绍了如何使用二维树状数组(差分数组)高效地处理矩阵的区间操作问题。通过维护四个树状数组,可以实现单点修改和区间查询,解决了给定矩阵中元素取反及查询特定区间内1的个数的问题。文章提供了详细的算法解释和代码实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

例题:B-逃离魔爪

题意:

给定矩阵大小,一开始全部元素都为0。操作1将一块区域取反;操作2回答区域有多少个1.

做法:

树状数组维护二维差分,将区间操作变为单点操作。

d(i,j)是差分数组。

差分数组的前缀和就是单点的值,而这里求的是区间的值,那就是差分数组前缀和后,变为单点的值,单点的前缀和才是区间的值。就是下面这个式子了。这个式子的意思是(x,y)的前缀和。

\begin{aligned} \sum_{i = 1}^{x} \sum_{j = 1}^{y} \sum_{h = 1}^{i} \sum_{k = 1}^{j} d(i)(k) =& \sum_{i = 1}^{x} \sum_{j = 1}^{y}d(i)(j) * (x - i + 1)*(y-j+1) \\=&(x+1)*(y+1)\sum_{i = 1}^{x} \sum_{j = 1}^{y}d(i)(j) \\&-(y+1)\sum_{i = 1}^{x} \sum_{j = 1}^{y}d(i)(j) *i \\&-(x+1)*\sum_{i = 1}^{x} \sum_{j = 1}^{y}d(i)(j) *j \\&+\sum_{i = 1}^{x} \sum_{j = 1}^{y}d(i)(j) *i*j \end{aligned}

为了方便维护,拆分为这四个式子。

类比一维的树状数组,tree(x)表示右端点为x,长为lowbit(x)的区间和。

二维的那就是,tree(x,y)表示右下角为(x,y),高lowbit(x),宽lowbit(y)的区间的区间和。

所以树状数组也就能进行二维的快速单点修改,区间查询了。

单点修改,需要改四个树状数组:

void point_add(int x, int y, int val)
{
    add(1, x, y, val);
    add(2, x, y, val * x);
    add(3, x, y, val * y);
    add(4, x, y, val * x * y);
}

 而一次区间加,需要转化为四个单点修改。至于为什么是四个点,可以看这个博客:二维差分

point_add(x1, y1, val);
point_add(x1, y2 + 1, -val);
point_add(x2 + 1, y1, -val);
point_add(x2 + 1, y2 + 1, val);

单点查询,查的是(x,y)的前缀和:

int point_que(int x, int y)
{
    int ans = 0;
    ans += que(1, x, y) * (x + 1) * (y + 1);
    ans -= que(2, x, y) * (y + 1);
    ans -= que(3, x, y) * (x + 1);
    ans += que(4, x, y);
    return ans;
}

区间查询太简单,就是二维前缀和,这里不放了。

放一下这题的代码:

#include <bits/stdc++.h>
#define int long long
using namespace std;
const int mod = 2, N = 1000 + 5;
int c[5][N][N];
int n, m;
void add(int k, int x, int y, int val)
{
    for (int i = x; i <= n; i += i & -i)
        for (int j = y; j <= m; j += j & -j)
        {
            c[k][i][j] += val;
            c[k][i][j] %= mod;
        }
}
int que(int k, int x, int y)
{
    int sum = 0;
    for (int i = x; i; i -= i & -i)
        for (int j = y; j; j -= j & -j) sum += c[k][i][j];
    return sum % mod;
}
void point_add(int x, int y, int val)
{
    add(1, x, y, val);
    add(2, x, y, val * x);
    add(3, x, y, val * y);
    add(4, x, y, val * x * y);
}
int point_que(int x, int y)
{
    int ans = 0;
    ans += que(1, x, y) * (x + 1) * (y + 1);
    ans -= que(2, x, y) * (y + 1);
    ans -= que(3, x, y) * (x + 1);
    ans += que(4, x, y);
    return ans;
}
signed main()
{
    scanf("%lld%lld", &n, &m);

    int q;
    cin>>q;
    int op;
    while (q--)
    {
        cin>>op;
        if (op == 1)
        {
            int x1, y1, x2, y2, val;
            val = 1;
            scanf("%lld%lld%lld%lld", &x1, &y1, &x2, &y2);
            point_add(x1, y1, val);
            point_add(x1, y2 + 1, -val);
            point_add(x2 + 1, y1, -val);
            point_add(x2 + 1, y2 + 1, val);
        }
        else
        {
            int x1, y1, x2, y2;
            scanf("%lld%lld%lld%lld", &x1, &y1, &x2, &y2);

            int ans = 0;
            ans += point_que(x2, y2);
            ans -= point_que(x1 - 1, y2);
            ans -= point_que(x2, y1 - 1);
            ans += point_que(x1 - 1, y1 - 1);
            printf("%lld\n", (ans % mod + mod) % mod);
        }
    }
    return 0;
}

### 树状数组单点更新区间求和的实现 树状数组是一种高效的数据结构,能够支持 **单点修改** 和 **区间查询** 操作,时间复杂度均为 \( O(\log n) \)[^1]。其核心思想是通过维护部分前缀和来快速计算任意区间的和。 #### 单点更新 对于单点更新操作,假设我们需要将位置 `i` 上的值增加一个增量 `delta`,可以通过以下方式完成: 1. 找到当前节点对应的范围,并将其值加上 `delta`。 2. 利用函数 `lowbit(x)` 计算下一个需要更新的位置,直到超出数组长度为止。 以下是具体代码实现: ```python def update(bit, i, delta): while i < len(bit): bit[i] += delta i += i & (-i) ``` 其中,`lowbit(i)` 是指整数 `i` 的二进制表示中最右侧的 1 及其后续的所有 0 构成的数值[^3]。 --- #### 区间求和 为了计算某个区间 `[l, r]` 的和,可以先利用树状数组计算整个序列的前缀和,再通过如下公式得到目标区间的和: \[ sum[l, r] = sum[1, r] - sum[1, l-1] \] 具体的前缀和查询逻辑如下所示: ```python def query(bit, i): res = 0 while i > 0: res += bit[i] i -= i & (-i) return res ``` 因此,要获取区间 `[l, r]` 的和,只需调用两次 `query()` 函数即可: ```python def range_query(bit, l, r): return query(bit, r) - query(bit, l - 1) ``` --- #### 完整示例代码 下面是一个完整的 Python 实现,展示了如何初始化树状数组并执行单点更新和区间查询操作: ```python class FenwickTree: def __init__(self, size): self.size = size self.bit = [0] * (size + 1) def update(self, i, delta): """Update the value at index i by adding delta.""" while i <= self.size: self.bit[i] += delta i += i & (-i) def query(self, i): """Query prefix sum up to index i.""" res = 0 while i > 0: res += self.bit[i] i -= i & (-i) return res def range_query(self, l, r): """Query the sum of elements from index l to r inclusive.""" return self.query(r) - self.query(l - 1) # Example usage if __name__ == "__main__": ft = FenwickTree(5) updates = [(1, 3), (2, 7), (3, 2), (4, 8), (5, 9)] for idx, val in updates: ft.update(idx, val) print(ft.range_query(2, 4)) # Output should be 7 + 2 + 8 = 17 ``` 上述代码定义了一个类 `FenwickTree` 来封装树状数组的功能,包括初始化、单点更新以及区间查询等功能。 --- #### 高级应用:二维树状数组 除了在一维场景下的应用外,树状数组还可以扩展至二维空间,用于解决更加复杂的矩阵问题。例如,在处理矩形区域上的加法和求和操作时,二维树状数组提供了一种高效的解决方案[^2]。 二维树状数组的核心思路是对每一行构建独立的一维树状数组,并在此基础上进一步叠加列方向的信息。这种设计使得它能够在 \( O((\log n)^2) \) 时间内完成相应的更新和查询操作。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值