树状数组笔记
树状数组是算法竞赛中常见的高级数据结构,是利用数的二进制特征进行检索的树状数据结构。常用于求解动态变化的区间问题。具有结构清晰,操作方便,应用灵活,效率高的特点。
单点修改+区间查询
这是树状数组最基础的应用,设 sum
的作用是求区间
void LSB(int x){return x&(-x);} void add(int x,int d){ while(x<=n){BIT[x]+=d;x+=LSB(x);} } int query(int x){ int ans=0; while(x){ans+=BIT[x];x-=LSB(x);} return ans; }
区间修改+单点查询
可以利用差分数组将区间修改变为单点修改。设
void LSB(int x){return x&(-x);} void add(int x,int d){ while(x<=n){BIT[x]+=d;x+=LSB(x);} } void change(int l,int r,int d){add(l,d);add(r+1,-d);} int query(int x){ int ans=0; while(x){ans+=BIT[x];x-=LSB(x);} return ans; }
区间修改+区间查询
这里需要两个树状数组才能高效完成 “区间修改+区间查询”,称为 “二阶树状数组”。
定义
因此我们可以维护两个树状数组,一个计算
void add(int x,int d1,int d2){ while(x<=n){ BIT1[x]+=d1; BIT2[x]+=d2; x+=LSB(x); } } int query1(int x){ int ans=0; while(x){ans+=BIT1[x];x-=LSB(x);} return ans; } int query2(int x){ int ans=0; while(x){ans+=BIT2[x];x-=LSB(x);} return ans; } void update(int l,int r,int d){ add(l,d,d*(l-1)); add2(r+1,-d,-d*r); } void query(int l,int r){ return r*query1(r)-query2(r)-(l-1)*query(l-1)+query2(l-1); }
二维 “区间修改+区间查询”
二维 “区间修改+区间查询” 是一维的拓展,推导方法和一维的类似。
令
此时
接下来推导区间和,以顶点为
可以看出问题的关键在于求解
此时需要维护四个树状数组,分别计算
void add(int x,int y,int d){ for(int i=x;i<=n;i+=LSB(i)){ for(int j=y;j<=m;j+=LSB(j)){ t1[i][j]+=d; t2[i][j]+=x*d; t3[i][j]+=y*d; t4[i][j]+=x*y*d; } } } void update(int a,int b,int c,int d,int delta){ add(a,b,delta); add(c+1,d+1,delta); add(a,d+1,-delta); add(c+1,b,-delta); } int sum(int x,int y){ int ans=0; for(int i=x;i;i-=LSB(i)){ for(int j=y;j;j-=LSB(j)){ ans+=(x+1)*(y+1)*t1[i][j]-(y+1)*t2[i][j]-(x+1)*t3[i][j]+t4[i][j]; } } return ans; } void query(int a,int b,int c,int d){ return sum(c,d)+sum(a-1,b-1)-sum(c,b-1)-sum(a-1,d); }
逆序对
逆序对指序列
我们把数字看作下标,每统计一个数字
如果值域太大,可以使用离散化。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具