支持随时修改某个元素的值,复杂度也为log级别。
来观察这个图:
令这棵树的结点编号为C1,C2...Cn。令每个结点的值为这棵树的值的总和,那么容易发现:
C1 = A1C2 = A1 + A2C3 = A3C4 = A1 + A2 + A3 + A4C5 = A5C6 = A5 + A6C7 = A7C8 = A1 + A2 + A3 + A4 + A5 + A6 + A7 + A8C16 = 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算这个2^k有一个快捷的办法,定义一个函数如下即可:int lowbit(int x){ return x&(x^(x–1));}
当想要查询一个SUM(n)(求a[n]的和),可以依据如下算法即可: step1: 令sum = 0,转第二步; step2: 假如n <= 0,算法结束,返回sum值,否则sum = sum + Cn,转第三步; step3: 令n = n – lowbit(n),转第二步。 可以看出,这个算法就是将这一个个区间的和全部加起来,为什么是效率是log(n)的呢?以下给出证明: n = n – lowbit(n)这一步实际上等价于将n的二进制的最后一个1减去。而n的二进制里最多有log(n)个1,所以查询效率是log(n)的。 那么修改呢,修改一个节点,必须修改其所有祖先,最坏情况下为修改第一个元素,最多有log(n)的祖先。 所以修改算法如下(给某个结点i加上x): step1: 当i > n时,算法结束,否则转第二步; step2: Ci = Ci + x, i = i + lowbit(i)转第一步。 i = i +lowbit(i)这个过程实际上也只是一个把末尾1补为0的过程。树状数组水题:
HDOJ 1166 敌兵布阵 http://acm.hdu.edu.cn/showproblem.php?pid=1166
View Code
1 #include <iostream>
2 #include <cstdio>
3 #include <cstring>
4 #define lowbit(x) x&(-x)
5 const int N=1000005;
6 int a[N],n;
7 using namespace std;
8 void add(int x,int y)
9 {
10 for(;x<=n;x+=lowbit(x))
11 a[x]+=y;
12 }
13 int sum(int x)
14 {
15 int sum=0;
16 for(;x>0;x-=lowbit(x))
17 sum+=a[x];
18 return sum;
19 }
20 int main()
21 {
22 int t,i,x,y,k=0;;
23 char s[101];
24 scanf("%d",&t);
25 while(t--)
26 {
27 k++;
28 memset(a,0,sizeof(a));
29 scanf("%d",&n);
30 for(i=1;i<=n;i++)
31 {
32 scanf("%d",&x);
33 add(i,x);
34 }
35 printf("Case %d:\n",k);
36 while(~scanf("%s",s))
37 {
38 if(s[0]=='E')
39 break;
40 scanf("%d%d",&x,&y);
41 if(s[0]=='A')
42 add(x,y);
43 else if(s[0]=='Q')
44 printf("%d\n",sum(y)-sum(x-1));
45 else if(s[0]=='S')
46 add(x,~y+1);
47 }
48 }
49 return 0;
50
2 #include <cstdio>
3 #include <cstring>
4 #define lowbit(x) x&(-x)
5 const int N=1000005;
6 int a[N],n;
7 using namespace std;
8 void add(int x,int y)
9 {
10 for(;x<=n;x+=lowbit(x))
11 a[x]+=y;
12 }
13 int sum(int x)
14 {
15 int sum=0;
16 for(;x>0;x-=lowbit(x))
17 sum+=a[x];
18 return sum;
19 }
20 int main()
21 {
22 int t,i,x,y,k=0;;
23 char s[101];
24 scanf("%d",&t);
25 while(t--)
26 {
27 k++;
28 memset(a,0,sizeof(a));
29 scanf("%d",&n);
30 for(i=1;i<=n;i++)
31 {
32 scanf("%d",&x);
33 add(i,x);
34 }
35 printf("Case %d:\n",k);
36 while(~scanf("%s",s))
37 {
38 if(s[0]=='E')
39 break;
40 scanf("%d%d",&x,&y);
41 if(s[0]=='A')
42 add(x,y);
43 else if(s[0]=='Q')
44 printf("%d\n",sum(y)-sum(x-1));
45 else if(s[0]=='S')
46 add(x,~y+1);
47 }
48 }
49 return 0;
50
HDOJ 1556 Color the ball http://acm.hdu.edu.cn/showproblem.php?pid=1556
View Code
1 #include <iostream>
2 #include <cstdio>
3 #include <cstring>
4 #define lowbit(x) x&(-x)
5 const int N=1000005;
6 int a[N],n;
7 using namespace std;
8 void add(int x,int y)
9 {
10 for(;x<=n;x+=lowbit(x))
11 a[x]+=y;
12 }
13 int sum(int x)
14 {
15 int sum=0;
16 for(;x>0;x-=lowbit(x))
17 sum+=a[x];
18 return sum;
19 }
20 int main()
21 {
22 int t,i,x,y,k=0;;
23 char s[101];
24 while(~scanf("%d",&n))
25 {
26 if(!n)
27 break;
28 memset(a,0,sizeof(a));
29 for(i=1;i<=n;i++)
30 {
31 scanf("%d%d",&x,&y);
32 add(x,1);
33 add(y+1,-1);
34 }
35 for(i=1;i<n;i++)
36 printf("%d ",sum(i));
37 printf("%d\n",sum(n));
38 }
39 return 0;
40
2 #include <cstdio>
3 #include <cstring>
4 #define lowbit(x) x&(-x)
5 const int N=1000005;
6 int a[N],n;
7 using namespace std;
8 void add(int x,int y)
9 {
10 for(;x<=n;x+=lowbit(x))
11 a[x]+=y;
12 }
13 int sum(int x)
14 {
15 int sum=0;
16 for(;x>0;x-=lowbit(x))
17 sum+=a[x];
18 return sum;
19 }
20 int main()
21 {
22 int t,i,x,y,k=0;;
23 char s[101];
24 while(~scanf("%d",&n))
25 {
26 if(!n)
27 break;
28 memset(a,0,sizeof(a));
29 for(i=1;i<=n;i++)
30 {
31 scanf("%d%d",&x,&y);
32 add(x,1);
33 add(y+1,-1);
34 }
35 for(i=1;i<n;i++)
36 printf("%d ",sum(i));
37 printf("%d\n",sum(n));
38 }
39 return 0;
40