树状数组

树状数组,顾名思义,树状的数组,其目的是优化区间查询和区间修改的复杂度。

看图

       C1 = A1

  C2 = A1 + A2

  C3 = A3

  C4 = A1 + A2 + A3 + A4

  C5 = A5

  C6 = A5 + A6

  C7 = A7

  C8 = A1 + A2 + A3 + A4 + A5 + A6 + A7 + A8

  ...

  C16 = A1 + A2 + A3 + A4 + A5 + A6 + A7 + A8 + A9 + A10 + A11 + A12 + A13 + A14 + A15 + A16

  这里有一个有趣的性质:

设节点编号为x,那么这个节点管辖的区间为2^k(其中k为x二进制末尾0的个数)个元素。因为这个区间最后一个元素必然为Ax,所以很明显:Cn = A(n – 2^k + 1) + ... + An

 

以9为例,C[9]=A[9]+C[8]即可;

而其中的k用了一个巧妙的方式,lowbit()

int lowbit(int x){

  return x&(x^(x–1));

  }

如:

x =1: 1 &-1(设位数为8)0000 0001 & 1111 1111 = 1

x = 6:6 & -6 0000 0110 &1111 1010 = 2 //补码&运算

总结一下,其实就是:

求出2^p(其中p: x 的二进制表示数中, 右向左数第一个1的位置),如6的二进制表示为110,向左数第零个为0,第一个为1,则p=1,故Lowbit(6) = 2^1 = 2。

其中最不好理解的就是数组的更新初始化(困扰快两天了,,,)当时一直不明白A[]数组如何储存的,如何转化成C[]数组的,其实根本不用储存A[]数组

用的是他的修改功能

void add(int x,int value)
{
         while(x<Max)
         {
                  c[x]+=val;
                  x+=lowbit;
         }
}

其中value就是对应的A[]数组

举个例子,数组1~10;如何将其储存在C数组中呢,只需要将1~10赋值给value即可

那么如何求和呢,下面是树状数组的第二个功能代码

 

int getsum(int k)
{
	int sum=0;
	while(k>0){
		sum+=d[k];
		k-=lowbit(k);
	}
	return sum;
}

这个就非常好理解啦!如果我想求1~10的和,只需传入10既可以啦~

那么如果我想求的区间不是从1开始呢

也非常简单,例如我要求区间[r,l]的区间和,那么就是getsum(l)-getsum(r-1);这里r一定要减一,不然就不包含r本身了。

其实就是那么简单啦,完整代码如下(例求1~10)的和

#include <iostream>
#include <cstring>
#include <algorithm>
#include <cstdio>

using namespace std;
int d[32005],l[32005];
int lowbit(int k)
{
	return (k&(-k));
}
void add(int pos,int v)
{
	while (pos <= 100 + 1)
	{
		d[pos]+=v;
		pos += lowbit(pos);
	}
}
int getsum(int k)
{
	int sum=0;
	while(k>0){
		sum+=d[k];
		k-=lowbit(k);
	}
	return sum;
}
int main(){
	
	memset(d,0,sizeof(d));
	for(int i=1;i<=100;i++)
	{
		int t;
		t=i;
		add(i,t);
	}
	cout<<getsum(10)<<endl;
	return 0;
	
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值