最近刚刚深入到了树状数组是什么,上年寒假学过,但是当时完全听不到,最近两天沉下心来,好好看了下,终于懂这个原理是什么,感觉并不是很难,而且很好用。
这个图应该更好理解:
d[1]=a[1];
d[2]=a[1]+a[2];
d[3]=a[3];
d[4]=a[1]+a[2]+a[3]+a[4];
依次类推,6管着5和6 8管着前8项,如果一个N (偶数)除以2还是一个偶数那他将是这个N项的和,如果是奇数的那他将管着N-1和第N项这两项的和
比如说对数组a,改变某一位的值需O(1),求某个k区间值O(k);
这样的m次操作是用O(m*k);
显然数据很大时,效率要差很多;
而树状数组,它改变某一位,或者求某个区间的和,都是O(logN);效率大为改善;
对于上图怎么计算区间的和;关键是需要几个函数;
先说树状数组最简单的用法,修改上面图中的某个点,并求某段区间的和;
下面介绍几个函数,用这几个来处理树状数组的问题。
第一个函数;
1 int lowbit(int x) 2 { 3 return x&(-x); 4 }
这个函数主要是用来求的是某个点管辖范围;
x&(-x); 对于& 可以百度下看看讲解,这个在这里就不多说了
如果是x+=x&(-x);就是得到的改点的父节点的值;比如x=4时;就能得到8;
而x-=x&(-x)的话,就是得到x这个点的管辖区间的下个区间的管辖点;
比如说,x=7,x&(-x)等于1,此时x=6不断循环到0能依次得到 6. 4.;
对于一个偶数他整除2还是偶数那么他将是前几项的和 它进行x&(-x)等于他自己的本身,奇数为1,可以自己手动打印几组观察下。
把他们所有的管辖区间正好是1....7;
第二个函数
1 void update(int x,int num) 2 { 3 while(x<=N) 4 { 5 d[x]+=num; 6 x+=lowbit(x); 7 } 8 }
这个函数,是用来修改树状数组的;从当前点开始到它的爸爸,爷爷,这样的长辈都得需要改变。
如果是一般的算法只用修改改点就可;但是树状数组必须修改所有改点被管辖的区间;
比如把a数组的 a[2]减去1,(令N=16);则所有2被管辖的点有4,8,16都应该减去1;
就是调用函数 update(2,-1);
第三个函数
1 int getSum(int x) 2 { 3 int s=0; 4 while(x>0) 5 { 6 s+=d[x]; 7 x-=lowbit(x); 8 } 9 return s; 10 }
这个函数就是求区间和了。。比如getSum(7)的话,就是求a[1]+a[2]+...a[7];
上面是最基本的用法;要学习树状数组必须把上面的过程原理搞明白;
它这样求和 比如7 会先加7 然后加6 然后加4 7包括自己 ,6包括5和6,4包括前四项,所以这样就求出来了前七项的和。
题目大体上可以分为两种(还有其他的就不多介绍了):
一,每次修改的是一个点,所求的是关于某段区间;
二,每次修改的是一个区间,所求的值是关于某个点的;
推荐博客:http://www.cnblogs.com/Penn000/articles/5758324.html
推荐题目:HDu 1556 Color the ball HDu 2689 sort it HDu 1166 poj2352 stars
相关题解在树状数组专栏有。