树状数组学习
树状数组与并查集类似,是一种数据结构,它可以用来维护前缀和问题
不过,利用前缀和和查分的思想,我们也可以用树状数组解决区间问题
与线段树不同,目前我暂且认为线段树解决最值问题,而树状数组解决求和问题
树状数组原理建立在二叉树上:利用lowbit运算实现向根节点保存的原理
介绍lowbit的程序实现只有三行就不打了
点击查看代码
int lowbit(int x){
return x&(-x);
}
下面是利用lowbit运算原理实现的add函数,很基础很简单
点击查看代码
void add(int x,int y){
while(x<=n){
t[x]+=y;
x+=lb(x);
}
}
点击查看代码
int ques(int x){
int ans=0;
while(x){
ans+=t[x];
x-=lb(x);
}
return ans;
}
下面是一维树状数组中常见的类型及模板
1.单点修改,区间查询
分析:这是基础的一类题,正好用上述三个操作来实现
Code
点击查看代码
#include <bits/stdc++.h>
using namespace std;
const int p=1e5+10;
char cmd[4];
int a[p],t[p];
int n,m,k,d;
int lb(int x){
return x&(-x);
}
void add(int x,int y){
while(x<=n){
t[x]+=y;
x+=lb(x);
}
}
int sum(int x){
int ans=0;
while(x){
ans+=t[x];
x-=lb(x);
}
return ans;
}
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
add(i,a[i]);
}
scanf("%d",&m);
while(m--){
scanf("%s%d%d",cmd,&k,&d);
if(cmd[0]=='A'){
add(k,d);
}
else{
cout<<(sum(d))-(sum(k-1))<<endl;
}
}
return 0;
}
不难看出
整体加对于前缀和的差量的影响只有区间左端的整体加和右端的减
因此我们通过这种思想把它转化为指令的单点修改,
开一个树状数组表示指令的累计影响,那么树状数组对应的下标i所有的指令前缀和就是所有指令对原数i的总影响
Code
点击查看代码
#include <bits/stdc++.h>
using namespace std;
const int p=100001;
int a[p], b[p];
int x,y,z,n,m;
char q[7];
void add(int x,int y,int z){
b[x]+=z;
b[y+1]-=z;
}
void ques(int x){
int ans=a[x];
for(int i=1;i<=x;i++){
ans+=b[i];
}
cout<<ans<<endl;
}
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
}
scanf("%d",&m);
for(int i=1;i<=m;i++){
memset(q,0,sizeof(q));
scanf("%s",q);
if(q[0]=='A'){
scanf("%d%d%d",&x,&y,&z);
add(x,y,z);
}
else if(q[0]=='Q'){
scanf("%d",&x);
ques(x);
}
}
return 0;
}
结束,你会了
不我不会
3.区间修改,区间查询(漏)
结合了上述两个的性质,开两个树状数组
一个用来维护区间修改,一个用来维护区间查询
Code
点击查看代码
#include <bits/stdc++.h>
using namespace std;
#define LL long long
const int N=1e5+10;
int n,belong[N],size;
LL A[N],sum[N],tag[N];
inline LL read(){
LL now=0,f=1;register char c=getchar();
for(;!isdigit(c);c=getchar())
if(c=='-') f=-1;
for(;isdigit(c);now=now*10+c-'0',c=getchar());
return now*f;
}
void Add(int l,int r,LL v){
for(int i=l;i<=min(r,belong[l]*size);++i)
A[i]+=v,sum[belong[i]]+=v;
if(belong[l]!=belong[r])
for(int i=(belong[r]-1)*size+1;i<=r;++i)
A[i]+=v,sum[belong[i]]+=v;
for(int i=belong[l]+1;i<belong[r];++i)
tag[i]+=v;
}
LL Query(int l,int r){
LL res=0;
for(int i=l;i<=min(r,belong[l]*size);++i)
res+=A[i]+tag[belong[i]];
if(belong[l]!=belong[r])
for(int i=(belong[r]-1)*size+1;i<=r;++i)
res+=A[i]+tag[belong[i]];
for(int i=belong[l]+1;i<belong[r];++i)
res+=sum[i]+tag[i]*size;
return res;
}
int main(){
n=read();
size=sqrt(n);
for(int i=1;i<=n;++i)
belong[i]=(i1)/size+1,A[i]=read(),sum[belong[i]]+=A[i];
int m=read(),l,r;
LL v;
char opt[6];
while(m--){
scanf("%s",opt);
l=read(),r=read();
if(opt[0]=='A')
v=read(),Add(l,r,v);
else
printf("%lld\n",Query(l,r));
}
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· winform 绘制太阳,地球,月球 运作规律
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人