当前位置: 首页 > web >正文

数据结构——树状数组(Binary Indexed Tree)

数据结构——树状数组(Binary Indexed Tree)

TS12 will see u on Oct.03

树状数组是一种可以快速区间修改和元素查询的结构,是线段树的低配版。原理是利用lowbit分解,大致感觉和二进制分解比较像,时间同样是logloglog级别的 。其实英文和它的作用更加贴切,就是通过二进制的索引产生像树一样的结构。

算法解释

树状数组的本质是用一个 “压缩版” 的树结构来存储数组的前缀和,核心是**lowbit **

关于lowbit

lowbit (x) 的定义:返回 x 的二进制表示中,最低位的 1 所对应的值。

比如:

  • x=5(二进制 101),最低位 1 在第 0 位(从 0 开始计数),对应值为 1,故 lowbit (5)=1;
  • x=6(二进制 110),最低位 1 在第 1 位,对应值为 2,故 lowbit (6)=2;
  • x=8(二进制 1000),最低位 1 在第 3 位,对应值为 8,故 lowbit (8)=8。

计算方式:由于计算机用补码存储负数,x & -x 即可直接得到 lowbit (x)(原理:-x 的补码是 x 取反 + 1,与 x 相与后仅保留最低位 1)。

代码:

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

接下来对照着树状数组的结构来说一下大概的操作流程。在这里插入图片描述

为了方便解释,我们把最原始的数据当做a数组,上边的"前缀和数组叫做“c数组”。可以看到,每一个c数组都是有一个管辖范围的,而查询就是通过不断爬这些管辖范围从后往前得到的。下面从一张表格直观的将“管辖范围”表示出来:

C 数组下标 ilowbit(i)管辖区间区间和
11[1,1]a[1]
22[1,2]a[1]+a[2]
31[3,3]a[3]
44[1,4]a[1]+a[2]+a[3]+a[4]
51[5,5]a[5]
62[5,6]a[5]+a[6]
71[7,7]a[7]
88[1,8]a[1]+a[2]+…+a[8]

单点添加,区间查询

P3374 【模板】树状数组 1 - 洛谷

如果说我们此时要求a[7]的前缀和,那么过程就是这样的: query[7]=c[7]+c[6]+c[4]。此时会发现通过lowbit从后往前爬刚好完全覆盖整个区间。

单点添加

如果我们要对某一个点进行添加,那么它会影响到它后边的元素。为什么呢?我们这样想:如果要求一个元素的前缀和,要对前面的所有可能的c数组进行相加。如果我对此时的点进行添加,那么对所有要用到此c数组的元素都进行相加。

具体代码

void add(int x, int y)//单点增加,只影响它自己和它后面的
{for(int i=x; i<=n; i+=lowbit(i))c[i]+=y;
}

区间查询

如果说我们此时要求某一段区间[L,R]之和的时候就可以先求query®再求query(L-1),最后query®-query(L-1)就是最终答案了。

具体代码

int query(int x)//查询,都是向前查找
{int ans=0;for(int i=x; i>0; i-=lowbit(i))ans+=c[i];return ans;
}

代码

// Problem: 洛谷P3374【模板】树状数组 1
// Contest: Luogu
// URL: https://www.luogu.com.cn/problem/P3374
// Memory Limit: 512 MB
// Time Limit: 1000 ms
// Submission Time: 2025-08-21 17:34:39#include <bits/stdc++.h>
using namespace std;
#define int long long
#define pii pair<int, int>
#define fi first
#define se second 
#define endl '\n'
const int INF = 1e18;
const int N = 1e6+5;
int c[N];
int n,m;
string s;
int lowbit(int x)
{return x&-x;
}
void add(int x, int y)//单点增加,只影响它自己和它后面的
{for(int i=x; i<=n; i+=lowbit(i))c[i]+=y;
}
int query(int x)//查询,都是向前查找
{int ans=0;for(int i=x; i>0; i-=lowbit(i))ans+=c[i];return ans;
}
void solve()
{cin >> n >> m;//数列的数字个数,操作个数for(int i=1; i<=n; i++)//此时的c[i]相当于是一个前缀和数组{int x;cin >> x;add(i,x);//输入的时候直接按照树状数组的结构,相当于单点添加了}int op;while(m--){int x,y;cin >> op >> x >> y;if(op==1) add(x,y);else cout << query(y)-query(x-1) << endl;}
}
signed main()
{ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);int T=1;// cin >> T;while(T--) solve();return 0;
}

区间修改,单点查询

差分转换

设计到区间修改的问题我们很快就能想到前缀和或者是差分。由于原来的c数组是一种类似于前缀和的数组,我们想要直接得到原始的值就需要进行差分。这样的话利用树状数组的特殊结构就能很快得到目标区间的和。主要是涉及到了差分的操作,其他还和原来是一样的。

代码

// Problem: 洛谷P3368【模板】树状数组 2
// Contest: Luogu
// URL: https://www.luogu.com.cn/problem/P3368
// Memory Limit: 125 MB
// Time Limit: 1000 ms
// Submission Time: 2025-08-22 19:09:06#include <bits/stdc++.h>
using namespace std;
#define int long long
#define pii pair<int, int>
#define fi first
#define se second 
#define endl '\n'
const int INF = 1e18;
const int N = 5e5+5;
int a[N],c[N];
int n,m;
string s;
int lowbit(int x)
{return x&(-x);
}
void add(int x, int y)
{for(int i=x; i<=n; i+=lowbit(i))c[i]+=y;
}int query(int x)
{int ans=0;for(int i=x; i>0; i-=lowbit(i))ans+=c[i];return ans;
}void solve()
{cin >> n >> m;for(int i=1; i<=n; i++) cin >> a[i];add(1,a[1]);//add操作会让它后边的全部加上这个数for(int i=2; i<=n; i++)//原来的c[i]相当于前缀和数组,经过差分之后变为正常数组了{int d=a[i]-a[i-1];add(i,d);}while(m--){int op;cin >> op;if(op==1){int l,r,k;cin >> l >> r >> k;add(l,k);if(r+1<=n) add(r+1,-k);}else{int x;cin >> x;cout << query(x) << endl;}}
}
signed main()
{ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);int T=1;// cin >> T;while(T--) solve();return 0;
}
http://www.xdnf.cn/news/18649.html

相关文章:

  • UE5多人MOBA+GAS 53、测试专属服务器打包和连接,以及配置EOS
  • WiFi有网络但是电脑连不上网是怎么回事?该怎么解决?
  • 云原生高级——K8S总概
  • OpenHands:开源AI软件开发代理平台的革命性突破
  • 2025最新版mgg格式转MP3,mflac转mp3,mgg格式如何转mp3?
  • setup 语法糖核心要点
  • Windows应急响应一般思路(一)
  • MySQL 高级主题:索引优化、ORM 与数据库迁移
  • More Effective C++ 条款02:最好使用C++转型操作符
  • 【0基础PS】蒙版与剪贴蒙版详解
  • NoCode-bench:自然语言驱动功能添加的评估新基准
  • 3.4 缩略词抽取
  • 表格识别技术:通过图像处理与深度学习,将非结构化表格转化为可编辑结构化数据,推动智能化发展
  • Vue Teleport 原理解析与React Portal、 Fragment 组件
  • GEO优化专家孟庆涛发布:《GEO内容优化的四大黄金标准》
  • 普中烧录软件 PZISP,打不开,提示“应用程序无法启动,因为应用程序并行配置不正确.....”
  • 学习嵌入式第三十五天
  • Linux应用软件编程---网络编程1(目的、网络协议、网络配置、UDP编程流程)
  • APP Usage『安卓』:比系统自带强10倍!手机应用使用时长精确到秒
  • MySQL - 视图,事务和索引
  • java8 findAny()、findFirst()空指针NullPointerException问题
  • ​维基框架 (Wiki Framework) 1.1.0 版本发布​ 提供多模型AI辅助开发
  • 图像指针:高效处理像素数据的核心工具
  • Linux虚拟机安装FTP
  • AtCoder Beginner Contest 419(ABCDEF)
  • Python Flask快速实现163邮箱发送验证码
  • 防火墙双机热备
  • 数据结构之深入探索快速排序
  • docker 打包
  • syn和quote的简单使用——生成结构体