树状数组实现 区间修改+区间查询




树状数组的本职:单值修改+区间查询


对于区间修改     首先想到的就是线段树   可是线段树的代码太tm长了    是真的懒得写    然后就学习了一下如何用状数组实现   区间修改+区间查询




差分数组


对于一个数组a     其差分数组定义为     C[i] = a[i] - a[i-1]( i>0 )&&C[0] = a[0]       

我们很容易看出    a[i] = C[0]+C[1]+C[2]+......+C[i]

然后   我们想实现区间修改就可以借助差分数组实现      

如果我们要实现对数组a    区间 [l,r]内的元素 +v

借助差分数组   只需使 C[l]+=v,C[r+1] -=v 即可





为什么可以这样实现呢     我们来看式子

a[1]+a[2]+...+a[n]

= (c[1]) + (c[1]+c[2]) + ... + (c[1]+c[2]+...+c[n]) 

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

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





由公式可以发现    我们要进行区间修改和区间查询只需要再维护一个数组 C2[i] = (i-1)*C[i]

对于         a[1]+a[2]+...+a[n]   =   n*read(C,n) - read(C2,n)




这样就可以进行区间修改和区间查询操作做了      因为我们同时进行修改数组C,C2    所以复杂度还是n*log(n)

而且比线段树快得多    




题目链接:http://codevs.cn/problem/1082/






代码实现:

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
using namespace std; 
typedef long long ll;
const int maxn=200000+10;
ll D1[maxn];
ll D2[maxn];
ll num[maxn];
ll n;
void add(ll D[],int k,ll num){
	while (k<=n){
		D[k]+=num;
		k+=(k&-k);
	}
}
ll read(ll D[],ll k){
	ll sum=0;
	while (k){
		sum+=D[k];
		k-=(k&-k);
	}
	return sum;
}
int main (){
	scanf ("%lld",&n);
	for (int i=1;i<=n;i++){
		scanf ("%lld",&num[i]);
		add(D1,i,num[i]-num[i-1]);
		add(D2,i,(i-1)*(num[i]-num[i-1]));
	}
	ll t;
	scanf ("%lld",&t);
	while (t--){
		ll m;
		scanf ("%lld",&m);
		if(m==1){
			ll l,r,v;
			scanf ("%lld%lld%lld",&l,&r,&v);
			add(D1,l,v);
			add(D1,r+1,-v);
			add(D2,l,v*(l-1));
			add(D2,r+1,-v*r);
		}
		else{
			ll l,r;
			scanf ("%lld%lld",&l,&r);
			ll sum1=(l-1)*read(D1,l-1)-read(D2,l-1);
			ll sum2=r*read(D1,r)-read(D2,r);
			printf ("%lld\n",sum2-sum1);
		}
	}
	return 0;
} 





你可能感兴趣的:(算法)