「ACM/OI」【数据结构】Update:1.24

本文介绍了树状数组在数据结构中的应用,包括单点修改和区间查询的不同场景,以及如何通过差分数组进行转换。文章详细解析了区间加求和和子矩阵操作的实现方法,提供了示例代码演示。
摘要由CSDN通过智能技术生成

ST表

树状数组

引入

例题2:区间加 求单点

分析
  • 用树状数组维护原数组的差分数组,对于区间 [ l , r ] [l,r] [l,r] 中每一个数加上 v a l val val,等价于差分数组d[]d[l] += val, d[r+1] -= val,这样区间修改的问题就转化成了例题1中的单点修改,而根据差分的性质, a [ i ] = ∑ j = 1 i d [ j ] a[i] = \sum_{j=1}^i d[j] a[i]=j=1id[j],故单点查询即转化成了区间求和。
代码

例题3:区间加 区间和

分析
  • 该问题可以使用两个树状数组维护差分数组解决。
  • 考虑序列 a a a 的差分数组 d d d,其中 d [ i ] = a [ i ] − a [ i − 1 ] d[i] = a[i] - a[i - 1] d[i]=a[i]a[i1]。由于差分数组的前缀和就是原数组,所以 a i = ∑ j = 1 i d j a_i=\sum_{j=1}^i d_j ai=j=1idj
  • 考虑将区间查询的差分转化。若要查询 a [ 1 ] + a [ 2 ] + … + a [ r ] a[1]+a[2]+ \ldots +a[r] a[1]+a[2]++a[r] ,即 ∑ i = 1 r a i \sum_{i=1}^{r} a_i i=1rai
  • 进行推导, ∑ i = 1 r a i = ∑ i = 1 r ∑ j = 1 i d j \sum_{i=1}^{r} a_i=\sum_{i=1}^{r} \sum_{j=1}^i d_j i=1rai=i=1rj=1idj,可以将此式展开易发现每个 d j d_j dj 出现了 r − j + 1 r-j+1 rj+1 次,所以上述式子可以进一步转化为 ∑ i = 1 r d i × ( r − i + 1 ) = ( r + 1 ) ∑ i = 1 r d i − ∑ i = 1 r i × d i \sum_{i=1}^r d_i\times (r-i+1) = (r+1)\sum_{i=1}^r d_i-\sum_{i=1}^ri\times d_i i=1rdi×(ri+1)=(r+1)i=1rdii=1ri×di
  • ∑ i = 1 r d i \sum_{i=1}^r d_i i=1rdi ∑ i = 1 r i × d i \sum_{i=1}^ri\times d_i i=1ri×di 分别可以用 trd[]trid[] 两个树状数组维护,每次查询其前缀和即可。而两个树状数组的单点修改:第一个数组trd[]就是例题2中常规的差分数组 trd[l] += val, trd[r+1] -= val,而后面一个不难发现增加或减少的值与下标相对应 trid[l] += l*val, trid[r+1] -= (r+1)*val
代码

二维树状数组

例题:

代码:

int n, m;
ll tr[maxn][maxn];

int lowbit(int x) { return x & -x; }

void update(int x, int y, ll val) {
    for (int i = x; i <= n; i += lowbit(i)) {
        for (int j = y; j <= m; j += lowbit(j)) {
            tr[i][j] += val;
        }
    }
}

ll sum(int x, int y) { // tr[1][1] + ... + tr[x][y] 子矩阵和
    ll res = 0;
    for (int i = x; i > 0; i -= lowbit(i)) {
        for (int j = y; j > 0; j -= lowbit(j)) {
            res += tr[i][j];
        }
    }
    return res;
}

ll query(int sx, int sy, int tx, int ty) {
    return sum(tx, ty) - sum(sx - 1, ty) - sum(tx, sy - 1) + sum(sx - 1, sy - 1);
}

void solve() {
    cin >> n >> m;
    int op;
    while (cin >> op) {
        if (op == 1) {
            int x, y;
            ll val;
            cin >> x >> y >> val;
            update(x, y, val);
        }
        else {
            int a,b,c,d;
            cin >> a >> b >> c >> d;
            cout << query(a, b, c, d) << "\n";
        }
    }
}
例题2 代码
例题3 代码
int n, m;
ll t1[maxn][maxn], t2[maxn][maxn], t3[maxn][maxn], t4[maxn][maxn];

int lowbit(int x) { return x & -x; }

void update(int x, int y, ll val) {
    for (int i = x; i <= n; i += lowbit(i)) {
        for (int j = y; j <= m; j += lowbit(j)) {
            t1[i][j] += val;
            t2[i][j] += val * x;
            t3[i][j] += val * y;
            t4[i][j] += val * x * y;
        }
    }
}

ll query(int x, int y) {  // tr[1][1] + ... + tr[x][y]
    ll res = 0;
    for (int i = x; i > 0; i -= lowbit(i)) {
        for (int j = y; j > 0; j -= lowbit(j)) {
            res += t1[i][j] * (x + 1) * (y + 1) - t2[i][j] * (y + 1) - t3[i][j] * (x + 1) + t4[i][j];
        }
    }
    return res;
}

void solve() {
    cin >> n >> m;
    int op;
    while (cin >> op) {
        if (op == 1) {
            int a, b, c, d;
            ll k;
            cin >> a >> b >> c >> d >> k;
            update(a, b, k);
            update(c + 1, d + 1, k);
            update(a, d + 1, -k);
            update(c + 1, b, -k);
        } else {
            int a, b, c, d;
            cin >> a >> b >> c >> d;
            cout << query(c, d) - query(a - 1, d) - query(c, b - 1) + query(a - 1, b - 1) << "\n";
        }
    }
}
  • 20
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值