警告:此处需要一定的二进制基础,二进制问题请右转百度
树状数组的储存有两个数组a[i],c[i],a[i]表示第i个数的大小
而c[i]则比较神奇,用下图表示
c[i]的地址(也就是i)最低位的位数决定了c[i],所能包含的数的多少,具体关系为:
c[i]包含数的个数==lowbit(i)的返回值
lowbit(i)返回的是i中最低位1的位置,可以通过机器补码的特性来完成
int lowbit(int x)
{
return x&-x;
}
构造一个初始的树状数组时,可以通过i+lowbit(i)的操作,像拨算盘进一位一样。保证得到的新数组的lowbit值更大且更高位相同。如图,
for(int i=1;i<=n;i++)
{
int x=i;
while(x<=n)
{
c[x]+=a[i];
x+=lowbit(x);
}
}
更新一个a[x]时,同时所有”lowbit值比x大且更高位相同”的数,也可以通过lowbit函数实现
int x,k;
scanf("%d%d",&x,&k);
int tmp=x;
a[x]+=k;
while(tmp<=n)
{
c[tmp]+=k;
tmp+=lowbit(tmp);
}
询问[x,y]区间和时,可以将问题拆分为求[1,x)和[1,y]的区间和,并相减。此处也可以通过lowbit函数实现
以[1,7]的区间求和为例,7(10)=111(2),所以x-=lowbit(x)会等于4,6,7,用图一表示为
int x,y;
scanf("%d%d",&x,&y);
int tmp,res;
tmp=y,res=0;
while(tmp>0)
{
res+=c[tmp];
tmp-=lowbit(tmp);
}
tmp=x-1;
while(tmp>0)
{
res-=c[tmp];
tmp-=lowbit(tmp);
}
printf("%d\n",res);
完整代码如下:
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
const int maxn=500010;
int a[maxn],c[maxn];
int lowbit(int x)
{
return x&-x;
}
int main()
{
int n,m;
cin>>n>>m;
for(int i=1;i<=n;i++)
scanf("%d",&a[i]);
for(int i=1;i<=n;i++)
{
int x=i;
while(x<=n)
{
c[x]+=a[i];
x+=lowbit(x);
}
}
for(int i=1;i<=m;i++)
{
int num;
scanf("%d",&num);
if(num==1)
{
int x,k;
scanf("%d%d",&x,&k);
int tmp=x;
a[x]+=k;
while(tmp<=n)
{
c[tmp]+=k;
tmp+=lowbit(tmp);
}
}
else
{
int x,y;
scanf("%d%d",&x,&y);
int tmp,res;
tmp=y,res=0;
while(tmp>0)
{
res+=c[tmp];
tmp-=lowbit(tmp);
}
tmp=x-1;
while(tmp>0)
{
res-=c[tmp];
tmp-=lowbit(tmp);
}
printf("%d\n",res);
}
}
return 0;
}
写的不好,如有问题,欢迎在评论区指出。