树状数组 PK 线段树

版权声明:八月炊火的博客如需转载请注明出处 https://blog.csdn.net/qq_34990731/article/details/82889930

这几天学树状数组,和之前的线段树比较发现树状数组虽然比线段树好实现,可是不支持区间查询和区间修改,那么树状数组就比不过线段树吗?想太多,之前有一些dalao吃饱了饭没事做于是就开始乱想,写过想出了超级树状数组,一个支持区间查询和区间修改的树状数组。我们之前做过区间查询和单点修改的树状数组和区间修改单点查询的树状数组(没看过的点这)那么有没有什么办法可以把它们和起来呢?答案是有的。
推导:我们想一下树状数组是怎么支持区间修改的,差分。我们设tree[i]=a[i]-a[i-1](tree是我们差分的数组,a是题目给的数组),可以知道a[i]=tree[1]+tree[2]+······+tree[i] 这样我们可以知道 a[1]+a[2]+……+a[i-1]+a[i]=tree[1]+(tree[1]+tree[2])+……+(tree[1]+……+tree[i])=(tree[1]×i)+(tree[2]×(i-1))+……(tree[i]×1)=i×(tree[1]+tree[2]+……+tree[i])-(tree[1]×0+tree[2]×1+……+tree[i]×(i-1))
这样我们只要维护两个树状数组(一个tree,一个tree1),tree用来维护tree[1]+tree[2]+……+tree[i]的区间和,tree1用来维护tree[1]×0+tree[2]×1+……+tree[i]×(i-1)的区间和(即tree1[i]=tree[i]×(i-1))这样原来的[1,i]就可以写成:i×(tree这个树状数组的[1,i]的区间和)-(tree1这个树状数组的[1,i]的区间和) 这样就好了,下面上代码, (题目):

#include<iostream>
#include<cstdio> 
using namespace std;
long long tree[100100],tree1[100100],n;//tree存数组的差分,tree1存tree数组中各自成自己下标 
long long lowbit(long long i)
{
	return i&(-i);
}
void change(long long *a,long long i,long long k)//因为要对两个树状数组维护为了简洁我们多传一维我们要维护的数组 
{
	while(i<=n)
	{
		a[i]+=k;
		i+=lowbit(i);
	}
	return ;
}
long long sum(long long *a,long long i)//这一题要求较高,要开longlong 
{
	long long ans=0;
	while(i>0)
	{
		ans+=a[i];
		i-=lowbit(i);
	}
	return ans;
}
int main()
{
	long long m,b=0,a=0,t,x,y,k;
	scanf("%lld %lld",&n,&m);
	for(int i=1;i<=n;i++)
	{
		scanf("%lld",&a);
		b=a-b;//我们用差分思想 
		change(tree,i,b);
		change(tree1,i,(i-1)*b);
		b=a; 
	}
	for(int i=1;i<=m;i++)
	{
		scanf("%lld",&t);
		if(t==1)
		{
			scanf("%lld %lld %lld",&x,&y,&k);
			change(tree,x,k);//因为是差分所以要修改两端 
			change(tree,y+1,-k);
			change(tree1,x,k*(x-1));
			change(tree1,y+1,y*(-k));
		}
		else 
		{
			scanf("%lld %lld",&x,&y);
			printf("%lld \n",((y*sum(tree,y)-(x-1)*sum(tree,x-1))-(sum(tree1,y)-sum(tree1,x-1))));//化简后的公式(自己动手算一下) 
		}
	}
	return 0;
 } 

希望这一篇题解可以给大家拓展一下思路,知道树状数组还可以这么玩,如果有疑问的话欢迎留言。

猜你喜欢

转载自blog.csdn.net/qq_34990731/article/details/82889930
今日推荐