Luogu P3368 【模板】树状数组 2

思路

树状数组2这道相当于是用树状数组来实现线段树的一部分功能(所以也可以用线段树来写),具体实现方法就是在树状数组上套一个差分。这看起来很简单,但是我们应该怎么做,而且又为什么要这么做呢?

至于为什么要这样做,是因为树状数组本质上只能完成单点修改和区间查询(其实单点查询也可以,只要把区间查询给定的区间改为只有一个数就可以了)。要完成区间修改,我们就需要差分这个东西

来帮助我们完成。差分是什么呢?一个数组的差分数组即为这个数组的每一项减它前面那一项(第一个数即为它本身)。对于一个差分数组,如果我们需要在L—R这段区间加上一个数K,那么我们就只需

要在L的位置加上K,在R+1的位置减去K即可。根据差分数组的性质,在L的位置加上K就相当于把从L开始到数组末尾的所有数都加上了K,在R+1的位置减去K就相当于把从R+1开始到数组末尾的所有数都

减去了K(这个东西自己理解一下,实在不行就背下来,因为很显然)。如果我们想知道某个位置的值,我们只要原数组做一次前缀和就可以得到。为什么这样做是可以的呢?我们来举一个例子:

给定原数组A:1,4,6,2,4,5(下标从1开始编号)

数组A的差分数组:1,3,2,-4,2,1

如果我们想把A从2—4加上5,那么我们就在A的差分数组的第二项3加上5得到8,在A的差分数组的第5+1=6项2减去5得到-3。

然后A的差分数组就会变为:1,8,2,-4,-3,1

我们再对这个差分数组做前缀和,就可以得到:1,9,11,7,4,5

观察一下上面的做完前缀和的数组,我们可以惊奇地发现,这个数组果然是数组A的2—4项都加上了5!这也就是为什么可以这么做(如果你不信还可以自己手推几组例子看一看,实在不行把数字换成字

母再推一遍(这样可信度可能会高一点,只要你不嫌麻烦))。

而树状数组的的求值恰好就是求从1—X所有数和(其实就是这个位置的前缀和),这个特性也使得差分这个东东对于树状数组尤其适用。至此这道题的所有核心就已经讲完了。具体实现看下面的代码。

Code

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<queue>
#include<map>
#define MAXN 500005
#define lowbit(x) x&(-x)
typedef long long ll;
int n,m,tree[MAXN];
inline void Insert(int x,int k){
	for(;x<=n;x+=lowbit(x))
		tree[x]+=k;
	return;
}
inline ll Query(int x){
	ll res=0;
	for(;x>=1;x-=lowbit(x))
		res+=tree[x];
	return res;
}
int main(){
	scanf("%d%d",&n,&m);
	int last=0;
	for(int i=1;i<=n;++i){
		int inp=0;
		scanf("%d",&inp);
		Insert(i,inp-last);
		last=inp;
	}
	for(int i=1;i<=m;++i){
		int opt=0;
		scanf("%d",&opt);
		if(opt==1){
			int x,y,k;
			scanf("%d%d",&x,&y);
			scanf("%d",&k);
			Insert(x,k);
			Insert(y+1,-k);
		}
		if(opt==2){
			int x=0;
			scanf("%d",&x);
			printf("%lld\n",Query(x));
		}
	}
	return 0;
}
posted @ 2020-07-26 20:03  Shadow_hyc  阅读(101)  评论(0)    收藏  举报