hdu 1166 敌兵布阵(我的第一道树形数组)

来源:互联网 发布:cellsens成像软件 编辑:程序博客网 时间:2024/06/05 19:51

树形数组,一直久闻其名。经常有人拿他和线段树做比较。就这道题来说,效率确实比线段树高了不少。

对于一个数组来说,如果我们想多次求它的一个区间的和的话,最好的方法是将a[i]中存入这个数组的前i项和。当我们想要求区间[x,y]内所有数据的和的话,我们只需要用a[y]减去a[x-1]就行了。但是这个方法的问题是,如果我们修改了其中第k项的值的话,那么我们就必须修改后面所有数据的值。

在线段树中,这种区间求和的问题很好处理,简直就是线段树最擅长处理的问题之一了。线段树对于任何一个值的修改效率是logn,而在修改完返回根节点的时候,顺路就更新了所有与这个节点相关区间的值。

树形数组则是用了另一种原理,a[i]存得值得数量是lowBit[i],lowBit是代码中的函数。这个方法优秀的地方是这个数值可以通过某种方式传递,类似于线段树中子节点的在返回根节点的过程中顺路修改一样,在树形数组中这样的修改效率是可以在logn的时间内完成的,在查询区间的值上,树形数组也有着不俗的效率,而且非常节约空间。

非常尴尬的是我似乎解释不了lowBit函数的原理。。。。

#include<stdio.h>#include<string.h>#define N 50005int a[N],b[N];int n;int lowBit(int x){return x&(-x);}int Sum(int x){int sum=0;while(x){sum+=a[x];x-=lowBit(x);}return sum;}int Find(int x,int y){return Sum(y)-Sum(x-1);}void Update(int x,int y){while(x<=n){a[x]+=y;x+=lowBit(x);}return ;}int main(){int T;scanf("%d",&T);int cnt=1;while(T--){scanf("%d",&n);int i;char s[10];memset(a,0,sizeof(a));for(i=1;i<=n;i++){int x;scanf("%d",&x);Update(i,x);}getchar();int flag=1;while(1){int x,y;scanf("%s",s);if(s[0]=='E')break;scanf("%d%d",&x,&y);if(s[0]=='A')Update(x,y);else if(s[0]=='S')Update(x,-y);else{if(flag){printf("Case %d:\n",cnt++);flag=0;}printf("%d\n",Find(x,y));}}}return 0;}