郝玩的数据结构2——树状数组(待upd)
首先,拉张图
树状数组,相对于线段树来说,空间复杂度更小,但是可以处理的信息具有局限性
常用于处理区间(矩阵)查改(差分转化为单点查改),单点查改
树状数组的关键其实就在于一个lowbit函数:求一个数二进制以最末的一个1的位置为指数2为底数的幂
(1)10=(1)2 (2)10=(10)2
so lowbit(1)=1 lowbit(2)=2 lowbit(3)=1 lowbit(4)=4 lowbit(5)=1 lowbit(6)=2 lowbit(7)=1 lowbit(8)=8......
如果一个数加上lowbit(这个数),那得到的数管理的节点中一定包含这个数
如果一个数减去lowbit(这个数),那得到的数管理的节点一定是从它前一个到不知道有多前的一段区间内的节点
emmm..所谓管理,其实就是成为祖先
欸,这个其实很神奇,如果你不相信这种魔法的话,你去看上面拉的图——
老登笔者讲的很屎,这里推荐一下董晓老师....去B站上搜,他讲的0基础也能听懂耶
由于笔者实在讲的太屎了,所以只能上个马了
(溜走
板子题1
Ac code:
点击查看代码
#include<bits/stdc++.h>
#define lowbit x&-x
using namespace std;
int n,m,s[500005];
void change(int x,int k){
while(x<=n) s[x]+=k,x+=lowbit;
}
int query(int x){
int t=0;
while(x) t+=s[x],x-=lowbit;
return t;
}
int main(){
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin>>n>>m;
int op,x,y;
for(int i=1;i<=n;i++){
cin>>y;
change(i,y);
}
for(int i=1;i<=m;i++){
cin>>op>>x>>y;
if(op==1) change(x,y);
else cout<<query(y)-query(x-1)<<"\n";
}
return 0;
}
以及改进(退/奇怪)了马蜂
点击查看代码
//树状数组
#include<bits/stdc++.h>
using namespace std;
#define N 500001
int n,m;
int s[N];
namespace tr{
inline int lowbit(int x){
return x&-x;
}
void change(int nb,int i){
while(i<=n){
s[i]+=nb;
i+=lowbit(i);
}
}
int query(int x){
int sum=0;
while(x){
sum+=s[x],x-=lowbit(x);
}
return sum;
}
}
namespace rw{
inline int read(){
int nb=0,f=1;
char c=getchar();
while(c>'9' || c<'0'){
if(c=='-') f=-f;
c=getchar();
}
while(c>='0' && c<='9'){
nb=(nb<<3)+(nb<<1)+(c^48);
c=getchar();
}
return nb*f;
}
void write(int x){
if(x<0) putchar('-'),x=~x+1;
if(x>9) write(x/10);
putchar(x%10^48);
return ;
}
inline void scan(){
n=read(),m=read();
for(int i=1;i<=n;i++){
int wow=read();
tr::change(wow,i);
}
for(int i=1;i<=m;i++){
int wow=read();
if(wow==1){
int x=read(),y=read();
tr::change(y,x);
}
else{
int x=read(),y=read();
cout<<tr::query(y)-tr::query(x-1)<<"\n";
}
}
}
}
int main(){
rw::scan();
return 0;
}
zro来更新了:
树状数组的区修点查
显然可见,区间修改的复杂度很高,所以我们可以使用差分的思想:修改x~y的值=修改差分数组x和y+1的值(如果不会差分,请学会)
而点查,差分数组的前缀和就是单点的值(如果不会差分,请学会)
Ac code:
点击查看代码
#include<bits/stdc++.h>
using namespace std;
const int N=5e5+10;
int n,m;
int a[N],s[N];
inline int lowbit(int x){
return x&(-x);
}
void change(int x,int k){
while(x<=n) s[x]+=k,x+=lowbit(x);
}
int query(int x){
int t=0;
while(x) t+=s[x],x-=lowbit(x);
return t;
}
int main(){
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin>>n>>m;
int op,x,y,k;
for(int i=1;i<=n;i++) cin>>a[i];
for(int i=1;i<=m;i++){
cin>>op>>x;
if(op==1){
cin>>y>>k;
change(x,k);
change(y+1,-k);
}
else cout<<a[x]+query(x)<<'\n';
}
return 0;
}
就酱!
下次更新:树状数组的区间加等差数列或者区修区查
加涅~
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 使用C#创建一个MCP客户端
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现