树状数组的基本知识已经被各种大牛和菜鸟讲到烂了,我就不多说了,下面给出基本操作的代码。
假定原数组为a[1..n],树状数组b[1..n],考虑灵活性的需要,代码使用int *a传数组。
#define lowbit(x) ((x)&(-(x)))
int sum(int *a,int x){
int s=0;
for(;x;x-=lowbit(x))s+=a[x];
return s;
}
void update(int *a,int x,int w){
for(;x<=n;x+=lowbit(x))a[x]+=w;
}
功能1:sum(x)返回原数组[1,x]的区间和,update(x,w)将原数组下标为x的数加上w。
这两个函数使用O(logn)的时间和O(n)的空间完成单点加减,区间求和的功能。
:功能2接下来做一些升级,让树状数组完成区间加减,单点查询的功能。
考虑将原数组差分,令d[i]=a[i]-a[i-1],特别地,d[1]=a[1]。
那么区间[l,r]整体加上k的操作就可以简单地使用d[l]+=k;d[r+1]-=k来完成了。
此时a[i]=d[1]+..+d[i],所以单点查询a[i]实际上就是在求d数组的[1..i]区间和,很容易完成了。
:功能3:下面再升级一次,完成区间加减,区间求和的功能。
仍然沿用d数组,考虑a数组[1,x]区间和的计算。d[1]被累加了x次,d[2]被累加了x-1次,...,d[x]被累加了1次。
因此得到sigma(a[i])=sigma{d[i]*(x-i+1)}=sigma{ d[i]*(x+1) - d[i]*i }=(x+1)*sigma(d[i])-sigma(d[i]*i)
所以我们再用树状数组维护一个数组d2[i]=d[i]*i,即可完成任务。
poj3468也可以用树状数组搞:实现功能3
#include <string>
#include <iostream>
#include <cstdio>
#include <cstring>
#define LL long long
#define N 100003
using namespace std;
LL a[N];
LL b[N];
LL c[N];
int n,m;
int lowbit(int x){
return x&(-x);
}
LL getsum(LL *a,int p)
{
LL ans=0;
for(int i=p; i>0; i-=lowbit(i))
ans+=a[i];
return ans;
}
void Modify(LL *a,int p,LL c)
{
for(int i=p; i<=n; i+=lowbit(i))
a[i]+=c;
}
int main()
{
while(scanf("%d%d",&n,&m)!=EOF)
{
for(int i=1; i<=n; i++)
scanf("%lld",&a[i]);
for(int i=1; i<=n; i++)
a[i]+=a[i-1];
char str[19];
LL L,R,w;
while(m--)
{
scanf("%s",str);
if(str[0]=='Q')
{
scanf("%lld%lld",&L,&R);
LL ans=a[R]-a[L-1]+(R+1)*getsum(b,R)-L*getsum(b,L-1)-getsum(c,R)+getsum(c,L-1);
printf("%lld\n",ans);
}
else
{
scanf("%lld%lld%lld",&L,&R,&w);
Modify(b,L,w);
Modify(b,R+1,-w);
Modify(c,L,L*w);
Modify(c,R+1,-(R+1)*w);
}
}
}
return 0;
}