树状数组定义

树状数组 ( F e n w i c k (Fenwick (Fenwick T r e e 或 B i n a r y Tree或Binary TreeBinary I n d e x e d Indexed Indexed T r e e ) Tree) Tree)
是一种高效的数据结构,用于动态维护前缀和或区间信息
其核心特点是通过二进制拆分优化单点更新和前缀查询操作
时间复杂度: O ( log ⁡ n ) O(\log n) O(logn)

核心(函数)部分

  • lowbit
    用于获取一个数二进制表示中最低位的1及其对应数值
    即按位与(&)

原理: 就是用x&-x补码表示,负数以补码形式存储
补码 = 负数 ( 二进制 ) 取反 + 1 补码=负数(二进制)取反+1 补码=负数(二进制)取反+1
当x与-x进行按位与操作时,会保留x的最低位的1,其余位均为0

int lowbit(int x){
	return x&-x;//按位与
}
  • sum
    用于计算从数组起始位置(通常为1)到指定位置的前缀和
    即区间查询

原理: 树状数组通过lowbit函数将原数组的索引转换为二进制形式,利用最低有效位 ( L S B ) (LSB) (LSB)的性质实现快速前缀和查询,每个节点存储的并非单点值,而是特定区间的部分和,通过叠加这些区间和得到最终结果

int sum(int x,int y[]){
    int ans=0;
    for(int i=x;i;i-=lowbit(i))
        ans+=y[i];
    return ans;
}//区间查询
  • update(add)
    用于单点更新(修改)
    即单点修改

原理: 树状数组的核心思想是通过二进制低位技术Lowbit来高效更新和查询,每个节点存储的是一个区间的和,而非单个元素的值,add操作通过不断调整索引来更新所有相关的区间和

void add(int x,int y,int z[]){
    for(int i=x;i<=n;i+=lowbit(i))
        z[i]+=y;
}//单点修改

这里注意先后顺序,先写 l o w b i t lowbit lowbit再写 s u m , a d d sum,add sum,add

例题展示

传送门 : 传送门: 传送门: 点这里 点这里 点这里

P3374 【模板】树状数组 1

题目描述

如题,已知一个数列,你需要进行下面两种操作:

  • 将某一个数加上 x x x

  • 求出某区间每一个数的和。

输入格式

第一行包含两个正整数 n , m n,m n,m,分别表示该数列数字的个数和操作的总个数。

第二行包含 n n n 个用空格分隔的整数,其中第 i i i 个数字表示数列第 i i i 项的初始值。

接下来 m m m 行每行包含 3 3 3 个整数,表示一个操作,具体如下:

  • 1 x k 含义:将第 x x x 个数加上 k k k

  • 2 x y 含义:输出区间 [ x , y ] [x,y] [x,y] 内每个数的和。

输出格式

输出包含若干行整数,即为所有操作 2 2 2 的结果。

输入输出样例 #1

输入 #1

5 5
1 5 4 2 3
1 1 3
2 2 5
1 3 -1
1 4 2
2 1 4

输出 #1

14
16

说明/提示

【数据范围】

对于 30 % 30\% 30% 的数据, 1 ≤ n ≤ 8 1 \le n \le 8 1n8 1 ≤ m ≤ 10 1\le m \le 10 1m10
对于 70 % 70\% 70% 的数据, 1 ≤ n , m ≤ 1 0 4 1\le n,m \le 10^4 1n,m104
对于 100 % 100\% 100% 的数据, 1 ≤ n , m ≤ 5 × 1 0 5 1\le n,m \le 5\times 10^5 1n,m5×105

数据保证对于任意时刻, a a a 的任意子区间(包括长度为 1 1 1 n n n 的子区间)和均在 [ − 2 31 , 2 31 ) [-2^{31}, 2^{31}) [231,231) 范围内。

样例说明:

故输出结果 14 14 14 16 16 16

分析

看题目
输入内容较多,逐行分析:
【line1】输入数列长度及操作数量
【line2】输入数列初值
【line3~line3+m】输入选择(choose)
如果是1,那么输入 x , k x,k x,k,使数列 t r e e [ x ] + = k ; tree[x]+=k; tree[x]+=k;
如果是2,那么输入 x , y x,y x,y,输出数列 t r e e [ x ] 到 t r e e [ y ] 的和 tree[x]到tree[y]的和 tree[x]tree[y]的和
思路解析
先看重点line3~line3+m
定义变量chose作为选择参数
判断
若为1则进行 a d d add add操作
若为2则进行 s u m sum sum操作
根据上文的函数模板,很轻松就可以 A C ( A c c e p t e d = 通过 ) AC(Accepted=通过) AC(Accepted=通过)

代码

#include<bits/stdc++.h>
using namespace std;
int n,m,tree[5000005];
int lowbit(int x){
    return x&(-x);//按位与
}
int sum(int x,int y[]){
    int ans=0;
    for(int i=x;i;i-=lowbit(i))
        ans+=y[i];
    return ans;
}//区间查询
void add(int x,int y,int z[]){
    for(int i=x;i<=n;i+=lowbit(i))
        z[i]+=y;
}//单点修改
int main(){
    cin>>n>>m;//执行【line1】操作
    for(int i=1;i<=n;i++){
        int a;
        cin>>a;
        add(i,a,tree);
    }//执行【line2】操作,用add一个一个赋初值
    for(int i=1;i<=m;i++){
        int chose;
        cin>>chose;//选择
        if(chose==1){//操作1
            int x,k;
            cin>>x>>k;
            add(x,k,tree);//修改/增加
        }else{//操作2
            int x,y;
            cin>>x>>y;
            cout<<sum(y,tree)-sum(x-1,tree)<<endl;
            //输出x~y的区间和(即1~y的和减去1~x-1的和)
        }
    }
    return 0;
}

题单提供

树状数组 树状数组 树状数组其中也有二叉堆,有空讲

~ 完结撒花 完结撒花 完结撒花 ~

附:例题和题单均来自 洛谷 洛谷 洛谷
内容实战可参考上方题单
下一篇预告:你猜

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐