Lowbit函数引用自 http://blog.csdn.net/int64ago/article/details/7429868
本周学习了树状数组,用了一个上午多的时间,终于算有点头绪了,用这么长时间,困扰我的是它的思想,并不是树状数组本身的算法。
学习完之后,简单说一下树状数组的思想,其实不要把它看的太高深,树状数组,顾名思义他就是一个数组,至于为什么叫树状数组接下来再说。学习树状数组的开始就应该把他当成一个存储的东西的数组,只是和普通数组的存储有些差别而已。怎么存储的呢?
其实树状数组的每个点的值都有相应的含义,比如a[1] 就是原数组的第一个值,但是a[2]就是原数组前两个的和,下标只要是奇数就和原数组的值相同,a[4]是原数组前四个的和,但a[6]是原数组第五个和第六个的和, ??? ???? ?????? 这是怎么回事,就可以这么认为,树状数组就是这么存的。 but ,规律是什么,那么a[8],a[10]代表的是谁的和?这到底是怎么算的?懵逼了有木有?此处就涉及到树状数组最精妙的设计之地。
树状数组最精妙的地方莫过于Lowbit函数,Lowbit(k)(k为下标)的值就代表了树状数组的第k个元素所表示的多少个原数组中元素的和。就是从原数组的第k个往前数Lowbit(k)- 1个元素的和。
Lowbit(k)函数的写法 就直接 return k & - k;
至于Lowbit函数的含义,有一篇经典博客附上网址:http://blog.csdn.net/int64ago/article/details/7429868 看完这篇博客基本上就能懂得树状数组是什么样子了。So,来个入门题比较好。
hdu 1166
1 10 1 2 3 4 5 6 7 8 9 10 Query 1 3 Add 3 6 Query 2 7 Sub 10 2 Add 6 3 Query 3 10 End
Case 1: 6 33 59
附上代码:
#include<iostream> #include<cstdio> #include<map> #include<math.h> #include<cstring> #include<algorithm> using namespace std; int a[50005], n; int Lowbit(int k) // 树状数组最精妙的设计精髓 { return k & -k; } void Update(int i, int val) // 操作函数 { //要把原数组的i个值加上val影响了它的上一级,所以要依次往上加 while(i <= n) { a[i] += val; i += Lowbit(i);//每次加上Lowbit(i)代表改变这次的值就是能影响的到树状数组的值 } } int sum(int i)//这个函数求得是从1到i所有元素的和 { int sum = 0; while(i > 0) { sum += a[i]; i -= Lowbit(i);//每次往下减,自己可以带入树状数组的a[6]试试。应该就能明白 } return sum; } int main() { int t, p = 1, i, a1, b, val; scanf("%d",&t); while(t--) { memset(a, 0, sizeof(a)); scanf("%d",&n); for(i = 1; i <= n; i++) { scanf("%d",&val); Update(i, val);//对于每次输入直接操作就可以。 } char str[50]; printf("Case %d:\n",p++); while(scanf("%s",str) != EOF) { if(strcmp(str,"End") == 0) break; scanf("%d%d",&a1,&b); if(strcmp(str,"Add") == 0) Update(a1,b); else if(strcmp(str,"Sub") == 0) Update(a1,-b); else { printf("%d\n",sum(b) - sum(a1 - 1));//前边见后边,大的减小的因为每次算的都是从1到b从1到a1-1的和 } } } return 0; }