树状数组学习

树状数组与并查集类似,是一种数据结构,它可以用来维护前缀和问题
不过,利用前缀和和查分的思想,我们也可以用树状数组解决区间问题
与线段树不同,目前我暂且认为线段树解决最值问题,而树状数组解决求和问题
树状数组原理建立在二叉树上:利用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);
  }
}
有了区间加还不够,我们还要实现查询,下面依旧是依靠lowbit运算原理实现的ques查询函数
点击查看代码
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;
} 
2区间修改,区间查询 这里用到差量变化的思想,考虑的是加减指令对于前缀和的影响

不难看出
整体加对于前缀和的差量的影响只有区间左端的整体加和右端的减
因此我们通过这种思想把它转化为指令的单点修改,
开一个树状数组表示指令的累计影响,那么树状数组对应的下标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;
}
咕了好久 ~~太颓了~~
posted @   2K22  阅读(35)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· winform 绘制太阳,地球,月球 运作规律
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
点击右上角即可分享
微信分享提示