[总结]树状数组的各种操作

说到维护动态区间求和(不管最大值最小值),那树状数组真的能比线段树简单很多。

看了一位学姐的总结真的收获好大(^-^)V

(1)首先树状数组的单点修改+区间查询 应该都会吧。即

update(l,x);
res=query(r)-query(l-1)

update和query这里就略过了,后面会有。


(2)区间修改+单点查询

其实跟(3)差不多,也就是维护一个delta数组用差分解决


(3)区间修改+区间查询

这个真的真的很有意思,以前我根本就不知道qwq,但现在发现好简单w。

我们用delta[i]表示i..n的共同增量。

那么利用差分思想在修改的时候只要

delta[l]+=x;
delta[r+1]-=x;
即可,重点就是在求和的地方

ans[i]=a[1]+a[2]+...+a[i]+delta[1]*i+delta[2]*(i-1)+...+delta[i]*1
        =sum(a[j])+sum(delta[j]*(i-j+1))
        =sum(a[j])+(i+1)*sum(delta[j])-sum(delta[j]*j)
for each 1<=j<=i
这样应该就显而易见了,sum(a[j])用前缀和维护,而sum(delta[j])和sum(delta[j]*j)用两个树状数组维护即可。

这样纸树状数组是不是很简单O(∩_∩)O

推荐去做做codeVS1082   以前用线段树打一脸懵逼的WA qwq



这里就附上区间修改+区间查询的代码吧,真心比线段树简单好多~


/*
作者:Leo
题目:p1082 线段树练习 3
*/
#include 
#include 
#include 
#include 
#include 
#define ll long long
#define N 200000+2000
using namespace std;
int n,Q;ll delta1[N],delta2[N],sum[N];
int read(){
    int x=0; char ch=getchar();
    while (ch<'0' || ch>'9') ch=getchar();
    while (ch>='0' && ch<='9'){x=x*10+ch-'0'; ch=getchar();}
    return x;
}
inline int lowbit(int x){return x & (-x);}
inline int update1(int i,int x){
	while(i<=n){
		delta1[i]+=x;
		i+=lowbit(i);
	}
}
inline int update2(int i,int x){
	while(i<=n){
		delta2[i]+=x;
		i+=lowbit(i);
	}
}
ll query1(int i){
	ll res=0;
	while(i>0){
		res+=delta1[i];
		i-=lowbit(i);
	}
	return res;
}
ll query2(int i){
	ll res=0;
	while(i>0){
		res+=delta2[i];
		i-=lowbit(i);
	}
	return res;
}
int main()
{
	n=read();
	for(int i=1;i<=n;i++){
		int x=read();
		sum[i]=sum[i-1]+x;
	}
	Q=read();
	while(Q--){
		int opt=read();
		if(opt==1){
			int l=read(),r=read(),x=read();
			update1(l,x);update1(r+1,-x);
			update2(l,x*l);update2(r+1,-x*(r+1));
		}else{
			int l=read(),r=read();
			ll sum1=sum[l-1]+l*query1(l-1)-query2(l-1);
			ll sum2=sum[r]+(r+1)*query1(r)-query2(r);
			printf("%lld\n",sum2-sum1);
		}
	}
	return 0;
}




你可能感兴趣的:(树状数组)