Binary Indexed Tree(树状数组)是能够完成下述操作的数据结构(单点更新,区间求和)
给一个初始值全为0的数列a1,a2,....an
*给定i,计算a1+a2+....ai(前i项和)
*给定i和x,执行ai+=x
通常用树状数组来快速求任意一区间的和
如果我们想求从s到t的和,只要知道(从1到t的和)和(从1到s-1的和)这2个数相减,就是我们要求的区间和
,也就是说,使用树状数组,我们可以快速求某一区间所有数的和。
37 |
24 | 13 |
8 | 16 | 10 | 3 |
5 | 3 | 7 | 9 | 6 | 4 | 1 | 2 |
如果想要得到前i项的和,如果是前8项,直接输出37,如果是前7项和,我们只需要知道红色部分的值分别是24,10,1。就可以了。
这样我们可以发现,我们查询的时候,每个节点右儿子的值不需要了,因为这个节点的值减去其左儿子的值就是其
右儿子的值。所以我们可以这样建立树状数组。。。
8 |
4 | 0 |
2 | 0 | 6 | 0 |
1 | 0 | 3 | 0 | 5 | 0 | 7 | 0 |
上面是红色数字的是我们要建数组的编号(元素值为0的就是可以省略的左儿子的值。。。。)
首先我们把数组下标转化成二进制。。。。来看看有什么发现。。。
1 0001
2 0010
3 0011
4 0100
5 0101
6 0110
7 0111
8 1000
我们可以发现以1结尾的1,3,5,7数组的长度是1,最后有1个0的2,6数组的长度是2(包含2个元素),而最后有2个0的4的长度是4(包含4个元素)。
这样在计算前i项和,需要从i开始,不断把当前位置i的值加入到结果中,并从i中减去i的二进制最低非0位对应的位,知道i变成0为止。
比如要计算前5项(二进制0101)的和,只需要知道bit[5和bit[4](0100 是0101减去0001得到的)的值就可以了
int bit[MAX_N],n;//区间是[1,n]
int sum(int i)//求前i项的和
{
int s=0;
while(i>0)
{
s+=bit[i];
i-=i&-i;//从i中减去i的二进制最低非0位对应的幂
}
return s;
}
void add(int i,int x)//给ai加上x
{
while(i<=n)
{
bit[i]+=x;
i+=i&-i;
}
}
二维BIT就是在用一个一维BIT数组管理n个其他一维BIT数组,就是说一维BIT数组里存储的是单个值。。。。。
而二维BIT数组里存储的是一个一维BIT数组。。。。
int bit[MAX_N][MAX_N],n,m;
int sum(int x,int y)
{
int res=0;
int i,j;
for(i=x;i>0;i-=i&-i)
for(j=y;j>0;j-=j&-j)
res+=bit[i][j];
return res;
}
int add(int x,int y,int v)
{
int i,j;
for(i=x;i<=n;i+=i&-i)
for(j=y;j<=m;j+=j&-j)
bit[i][j]+=v;
}
此外还有 一维,二维的区间更新,单点查询。
int bit[MAX_N];
int sum(int x)
{
res=0;
while(x>0)
{
res+=bit[x];
x-=x&-x;
}
}
int add(int x,int v)
{
while(x<=n)
{
bit[x]+=v;
x+=x&-x;
}
}
int update(int x,int y,int v)
{
add(x,v);
add(y+1,-v);
}
int num[MAX_N];
int bit0[MAX_N];
int bit1[MAX_N];
int sum(int *a[],int x)
{
res=0;
while(x>0)
{
res+=bit[x];
x-=x&-x;
}
}
int add(int *a[],int x,int v)
{
while(x<=n)
{
bit[x]+=v;
x+=x&-x;
}
}
void init()
{
for(i=1;i<=n;i++)
add(c0,i,num[i]-num[i-1]);
add(c1,i,(i-1)*(num[i]-num[i-1]));
}
void update(int x,int y,int v)
{
add(c0,x,v);add(c0,y+1,v);
add(c1,x,v*(x-1));add(c1,y+1,-y*v);
}
int query(int *a[],int *b[],int l,int r)
{
int sum0,sum1;
sum0=(l-1)*sum(a,l-1)+sum(a,l-1);
sum1=r*sum(b,r)+sum(b,r);
return sum1-sum0;
}
网上的大多数讲解不全面 ,特此给大家分享一个大神帖,把树状数组的所有变种都涵盖到了。。很全面