笔记——树状数组

蓝月の笔记——树状数组

可恶的OI里,我们尝尝会遇到一些区间问题,例如区间修改单点查询,单点修改区间查询,区间修改单点查询,单点修改单点查询

其中,单点修改区间查询,就是树状数组最经典的用法啦!

Luogu - P3374

给定一个长度为 n 的序列 a1,a2,,an 和两种操作:

  1. 输入 1 x kak 加上 x
  2. 输入 2 l r,求 i=lrai

这就是我们说的单点修改区间查询

根据前缀和的思想,我们可以用一些手法求出每一个 [1,x] 的和。查询时直接输出 Q(r)Q(l) 即可。(Q(x) 为查询 i=1xai 的函数)

首先我们设树状数组的数组(?)为数组 c

定义 cx 表示树状数组这棵树中 x 的所有子节点的 ci 总和,即 cx=iison of xci


lowbit

我们引入一个函数,叫 lowbit,从避免可知,lowbit 就是最 low()bit(二进制位),即二进制中最低位的 1

举个例子:

x=18=(10010)2

lowbit(x)=2=(10)2

lowbit 怎么求呢,这时候就要用到补码的特性了。

我们注意到 x(x)+1 的二进制,我们求的最低位的 1 一直到最后一位都没有变化,这一段是 (1000)2,而前面的部分全部取反。

我们充分发扬人类智慧,将 x(x)+1 按位与,就可以把前面的全部消掉,而留下 (1000)2 这一部分,而这,就是我们要求的 lowbit

而根据补码的性质 (x)+1=(x),所以我们可以得到求 lowbit 的代码:

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

如果你喜欢写成宏定义的话:

#define L(x) ((x) & (-x))

还是已 x=18 为例:

lowbit=(10010)2&(01110)2

10010

&01110

__________

00010=2

我们前面手算的也是 2,这就证明了 lowbit=x&x


update

先放图。

【图片转载自liruixiong0101的blog

观察这棵树,可以发现 ci 的父亲为 ci+lowbit(i),而根据 c 的定义 cx 一定在 ci+lowbit(i) 里面,然后我们循环迭代递推,所以我们可以得到 update 的代码:

void U(int x, int y) {
  for (int i = x; i <= n; i += L(i)) {
    c[i] += y;
  }
}

query

根据设计,query 求的是 i=1xai 而不是 i=lrai!!!

根据定义,cx 代表 i=xlowbit(x)+1xai,我们可以把 query 操作抽象成跳跃的过程。

我们现在 x 的位置,我们的目标是跳到 1,根据 c 的定义,我们先往前跳 lowbit(x) 格,也就是跳过 cx 管辖的区间,这时 anscx

现在我们在 xlowbit(i) 的位置,我们令 ixlowbit(i),我们再往前跳 lowbit(i) 格,正好跳过 ci 的管辖区间,这时 anscx+ci

一直持续这个操作,直到 i0,我们就得到了 [1,x] 之间不重不漏的区间和了。

根据思路写出代码:

int Q(int x) {
  int ans = 0;
  for (int i = x; i; i -= L(i)) {
    ans += c[i];
  }
  return ans;
}

然后求 i=lrai 就可以很轻松了,即 Q(r) - Q(l - 1)


code

然后我们就可以写完这道水黄力!(喜

已用结构体封装好的代码:

// P3374
#include<bits/stdc++.h>

using namespace std;

const int kMaxN = 5e5 + 5;

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

struct BIT {
  int n, a[kMaxN], c[kMaxN];
  void U(int x, int y) {
    for (int i = x; i <= n; i += L(i)) {
      c[i] += y;
    }
  }
  int Q(int x) {
    int ans = 0;
    for (int i = x; i; i -= L(i)) {
      ans += c[i];
    }
    return ans;
  }
};

int m, op, l, r;
BIT t;

int main()
{
  cin >> t.n >> m;
  for (int i = 1; i <= t.n; i++) {
    cin >> t.a[i];
    t.U(i, t.a[i]);
  }
  for (; m; m--) {
    cin >> op >> l >> r;
    if(op == 1) {
      t.U(l, r);
    } else {
      cout << t.Q(r) - t.Q(l - 1) << '\n';
    }
  }
  return 0;
}

友情给出树状数组结构体:(lowbit 要自己写)

struct BIT {
  int n, a[kMaxN], c[kMaxN];
  void U(int x, int y) {
    for (int i = x; i <= n; i += L(i)) {
      c[i] += y;
    }
  }
  int Q(int x) {
    int ans = 0;
    for (int i = x; i; i -= L(i)) {
      ans += c[i];
    }
    return ans;
  }
};
posted @   BluemoonQwQ  阅读(53)  评论(2编辑  收藏  举报
相关博文:
阅读排行:
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· DeepSeek在M芯片Mac上本地化部署
点击右上角即可分享
微信分享提示