C++树状数组 (原理 + 代码 + lowbit解释)

目录:        

        什么是树状数组?

                代码模板

                         原理 + lowbit解释

什么是树状数组?

        树状数组作为一种高效的数据结构,可以在O(logn)内完成更新和查询操作,因此非常适合加减, 区间和, 查询。

适合问题:动态连续和查询问题:给定一个n个元素的数组A1,A2,…,An,你的任务是设计一个数据结构,支持以下两种操作。

  1. add(x,d)操作:让Ax增加d
  2. query(L,R):计算AL+AL+1+…+AR

代码模板

struct BIT {
  int n;                            // 树的大小
  vector<LL> C;                     // 存储树的节点值
  BIT(int _n) : n(_n), C(_n + 2) {} // 构造函数,初始化树的大小和节点值
  inline int lowbit(int x) { return x & -x; } // 计算二进制中最低位的1对应的整数
  void add(int x, LL v) {                     // 在位置x上增加值v
    while (x <= n)
      C[x] += v, x += lowbit(x); // 遍历x的所有上级节点,更新节点值
  }
  LL sum(int x) { // 计算从1到x的所有值的和
    LL s = 0;
    while (x)
      s += C[x], x -= lowbit(x); // 遍历x的所有下级节点,累加节点值
    return s;
  }
};

!!!推荐大家自己动手敲一遍, 尽量背。

原理解释

构造函数

对这个感兴趣的可以去关注博客, 有六季类与结构体的专题。

这里简单讲一下概念, 构造函数就是在构造和访问函数时运行的代码。

这里, 的这个写法意思就是在结构体中查询有一个名叫num的变量初始化是num(),再括号里填入_num, num 是个 int 变量 所以等价于 num = _num, C 是个 vector 所以等价于 C.resize(_num + 2)

student(int _num):num(_num){}

再来看一下lowbit

lowbit

详细链接 : https://www.kdocs.cn/l/cvTe0OBF81XO?openfrom=docs

概念:

对于正整数x,我们定义lowbit(x)为x的二进制表达式中最右边的1所对应的值(而不是这个比特的序号)。比如,38 288的二进制是1001010110010000,所以lowbit(38 288)=16(二进制是10 000)。在程序实现中,lowbit(x)=x&-x。为什么呢?回忆一下,计算机里的整数采用补码表示,因此-x实际上是x按位取反,末尾加1以后的结果,如下图所示。

二者按位取“与”之后,前面的部分全部变0,之后lowbit保持不变。如下图所示是一棵典型的BIT,由15个结点组成,编号为1~15。

 

其余的可以访问链接, 但最好还是自己动手演算一下。

                                           ——摘自     https://www.kdocs.cn/l/cvTe0OBF81XO?openfrom=docs

### 关于C++树状数组进行区间修改的方法 为了实现基于树状数组的区间修改操作,在原有基础上引入差分数组的概念可以有效简化问题。对于给定的一个长度为n的序列a,构建其对应的差分数组b满足如下关系: \[ b[i] = a[i] - a[i-1], \quad i = 2,3,...,n \] 当对原数组执行一次区间增加时,仅需对该区间的起始位置和结束位置之后的第一个元素在差分数组上做相应的调整即可完成整个区间的增量更新[^3]。 具体到代码层面,可以通过定义两个函数`update`来分别处理单点增减以及求前缀和的操作。这里需要注意的是,由于涉及到区间修改,因此还需要额外维护一个懒惰标记用于记录尚未应用至实际数据结构上的变化量。然而,针对简单的区间加法场景,则可以直接利用上述提到的差分特性而无需显式地管理这种延迟传播机制。 下面是具体的实现方式: ```cpp #include<iostream> using namespace std; #define lowbit(x) ((x)&-(x)) #define N 10010 int diffTree[N]; // 差分树状数组 // 更新某一点及其后续影响范围内的值 void pointUpdate(int idx, int delta){ while(idx<N){ diffTree[idx]+=delta; idx += lowbit(idx); } } // 获取指定索引处累积的变化总量 int prefixSum(int idx){ int res=0; while(idx>0){ res+=diffTree[idx]; idx -= lowbit(idx); } return res; } // 计算某个范围内所有数目的总和(通过计算两端点的前缀和相减得到) int rangeQuery(int l, int r){ return prefixSum(r)-prefixSum(l-1); } ``` 此段程序展示了如何借助树状数组配合差分技巧高效解决区间修改的问题。每当需要改变一段连续子列中的数值大小时,只需调用pointUpdate()方法作用于该片段边界节点之上;而对于任意一对端点所界定部分内各成员累加之和的需求,则可通过rangeQuery()接口快速获取结果。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值