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

来源:互联网 发布:矢仓枫子 知乎 编辑:程序博客网 时间:2024/05/22 05:00




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


对于区间修改     首先想到的就是线段树   可是线段树的代码太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 <iostream>#include <stdio.h>#include <string.h>#include <stdlib.h>#include <map>#include <set>#include <vector>#include <algorithm>#include <stack>#include <queue>#include <math.h>#include <bitset>#include <climits>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;} 





原创粉丝点击