---恢复内容开始---
http://poj.org/problem?id=2352
这是一个树状数组的题目,也是我第一次接触这类的题目,也正是因为之前的一道题,TLE了,超时太严重了,我看discuss里再说什么用树状数组可以我才去找有关于这方面的博客看
看了很多博客才稍微的理解了一点树状数组,可以在以后的做题中,加强对这个的理解
这个图片就是数组数组的内涵所在
其中A数组就是原数组,而C[n]=a[n-2^k+1]+.......+a[n]的值,其中K为二进制下的末尾0的个数,比如c[8],8(1000)有3个0,所以,k=3,则C[8]就表示从a[1]到a[8]的值;
int lowbit(int x)
{
return x&(-x); //这个就是用来返回2^k的值的。
}
而对于查询来说的话
求(1,8)的值就是为C[8]
而求(4,8)的值时则可以用C[8]-c[3]-c[2];
也就是sum(a,b)=sum(1,b)-sum(1,a-1);
1 int sum(int end) //计算原数组1-end的和 2 { 3 int sum=0; 4 while(end>0) 5 { 6 sum+=c[end]; 7 end-=lowbit(end); 8 } 9 return sum; 10 }
关于上面的第6-7行,我觉得举两个实例更加容易理解
比如说要求1-8的和也就是c[8];
首先end=8;
也就是sum=c[8];
而lowbit=8,所以,sum=c[8];
而当end=7;时
首先sum=c[7];
而7(111)所以C[7]=a[7];
lowbit(7)=1;
所以接下来就是sum=c[7]+c[6];
6(110)所以,c[6]=a[5]+a[6];
end=4;
在接下来就是sum=c[7]+c[6]+c[4];
4(100)所以,c[4]=a[1]+....+a[4];
end=0;
不得不说,这个确实很巧妙
对于更新的话,这个只有一个点一个点的进行更新
下面为从a[pos]的位置上加一个num:
1 void update(int pow,int num) 2 { 3 while (pow<=n) 4 { 5 c[pow]+=num; 6 pow+=lowbit(pow); 7 } 8 }
其实有了上面的的举例之后,也可以感觉得到在某个位置上加个数之后,只不过是在被C包围的那个位置上加上个数,所对应的C也自然而然的加,
且这个有局限性就是只可以对于单个节点进行更新
对于二维数组数组
下面的代码给a[i][j]这一节点加上个K
1 void update(int i,int j,int k) 2 { 3 while (i<=n) 4 { 5 int temp=j; 6 while(temp<=n) 7 { 8 c[i][temp]+=k; 9 temp+=lowbit(temp); 10 } 11 i+=lowbit(i); 12 } 13 }
下面就是关于POJ2352的一个解题代码
1 #include <stdio.h> 2 #include <iostream> 3 4 using namespace std; 5 6 int n; 7 int level[32001]; //level就是用来存等级的 8 int c[32001]; //树状数组 9 int lowbit(int x) 10 { 11 return x&(-x); 12 } 13 14 int sum(int x) //求和 15 { 16 int s=0; 17 while(x>0) 18 { 19 s+=c[x]; 20 x-=lowbit(x); 21 } 22 return s; 23 } 24 25 void update(int pos) //更新,因为树状数组么,所以它肯定是更新到最顶端! 26 { 27 while(pos<=32001) 28 { 29 c[pos]++; 30 pos+=lowbit(pos); 31 } 32 } 33 int main() 34 { 35 scanf("%d",&n); 36 int x,y; 37 for(int i=1;i<=n;i++) 38 { 39 scanf("%d%d",&x,&y); 40 level[sum(x+1)]++; //每次多一个之后,sum(x+1)所对应的那个等级就会++,而为什么是X+1的目的就是避免X=0,而且不会影响其的相对位置,如果不理解,多看几遍就会懂其内涵 41 update(x+1); 42 } 43 for(int i=0;i<n;i++) 44 { 45 printf("%d ",level[i]); 46 } 47 return 0; 48 }