树状数组-区间查询+区间修改

听说树状数组可以支持区间加??今天特地跑去这里学习了一下,%%%%%%%%%%%%%,下面结合我的理解再讲一讲

有关树状数组的基础知识我就不赘述了,想必大家都明白,如果不清楚可以自己百度,毕竟这不是蒟蒻三言两语就可以讲通的

那现在假设你已经会了树状数组的 “ 单点修改,区间询问 ” ,我们就来讲一讲升级版的 “ 区间修改,区间询问 ”

【写在前面】

区间修改:

我们让 sigma (r ,j )表示 r 数组的前 j 项和,即sigma (r ,j )=r [ 1 ] + r [ 2 ] +r [ 3 ]+……+r [ j ]

用 a 数组表示原序列,其差分序列为 c 数组,即 c [ i ]= a [ i ] - a [ i - 1 ] 

显然,a [ j ] = sigma(c ,j )

然后我们发现若要对序列中的 i ~ j 个数进行操作(比如都加上一个 v ),其实就是 c [ i ] + =  v , c [ j + 1 ] - = v,因为其他在 i ~ j 中间的数由于都加了一个 v ,其对应的 c 数组不会改变  

【写在中间】

区间查询:

扫描二维码关注公众号,回复: 2856922 查看本文章

首先使用前缀和相减这不用多说,那我们就来考虑如何求 1 ~ q 这个区间内各个数的和

ans = a[1] + a[2] + a[3] +……+ a[q-1] + a[q]

       =sigma(c,1) + sigma(c,2) + sigma(c,3) +…… + sigma(c, q-1 ) + sigma(c, q )

       =c[1] + ( c[1] + c[2] ) + ( c[1] + c[2] + c[3] ) + …… + (c[1] + c[2]+……+ c[q-1] )+ ( c[1] + c[2]+……+ c[q] )

      = q*c[1] + (q-1)*c[2] + (q-2)*c[3] +…… + c[q]

     = q* (c[1]+c[2]+...+c[q]) - (0*c[1]+1*c[2]+...+(q-1)*c[q]) 

       A                                                     B

A部分很好办,就是一般的树状数组求前缀和

而对于B部分,我们可以将其看作一个新的数组c2[i]=(i-1)*c[i],然后照例维护一下即可

每当修改c的时候,就同步修改一下c2,这样复杂度就不会改变

所以 ans=q*sigma(c,q)-sigma(c2,q)

【写在最后】

例题:洛谷3372

其实任何一道区间加和区间询问的题都可以交上去试一下

具体代码:

#include<cstdio>
#include<cmath>
#include<algorithm>
#define ll long long
#define N 100009
using namespace std;
int lowbits(int x){return x&(-x);}
int n,m;
ll c1[N],c2[N],a[N]; 
void add(ll *t,ll k,int i){
	while(i<=n){	t[i]+=k;	i+=lowbits(i);	}
}
ll querysum(ll *t,int x){
	ll res=0;
	while(x){ res+=t[x];x-=lowbits(x);	}
	return res;
}
int main(){
	scanf("%d%d",&n,&m);
	int i,j,k;
	for(i=1;i<=n;++i){
		scanf("%lld",&a[i]);
		add(c1,a[i]-a[i-1],i);
		add(c2,(i-1)*(a[i]-a[i-1]),i);///////中间不可以写成(i-1)*c1[i],因为这里的c1[i]不是我们定义的那个,它是树状数组里的,包含了他前面的值的
	}
	for(i=1;i<=m;++i){
		int ty,x,y;
		ll z;
		scanf("%d%d%d",&ty,&x,&y);
		if(ty==1){
			scanf("%lld",&z);
			add(c1,z,x);add(c1,-z,y+1);
			add(c2,(x-1)*z,x);////和上面同理
			add(c2,-y*z,y+1);
		}
		else{
			ll ans;
			ans=y*querysum(c1,y)-querysum(c2,y);
			ans-=(x-1)*querysum(c1,x-1)-querysum(c2,x-1);
			printf("%lld\n",ans);
		}
	}
	return 0;
}

那这个代码和线段树的一比,你就知道为什么我要学了,嘿嘿

猜你喜欢

转载自blog.csdn.net/weixin_42557561/article/details/81781916