說樹狀數組其實是一個索引表,但是是一個特殊的,樹狀的索引表,它利用了二進制的一些特性。
就區間求和的要求來說:
首先我們用a[]數組來存儲原始數據。然后在a[]之上構造c[]數組來作為樹狀數組。
如圖

這個圖表示,當i為奇數時,c[i]中保存的都是a[i]本身。然后,c[2]中保存了a[1], a[2],共2個,c[4]中保存的是a[1], a[2], a[3], a[4],c[6]又是保存兩個,c[5]和c[6]。c[8]保存8個,c[1], c[2], c[3], c[4], c[5], c[6], c[7], c[8]。
看出什么規律沒有?
如果將這些下標差成2進制序列就容易看出來規律了——
如果從右向左看,第一個是1的數位是第1位,那么c[i]中只有1個數,如果第一個是1的數位式第2位,那么c[i]中有2個數,如果是3,那么c[i]中有4個數,以此類推。
但是怎么代碼怎么實現呢?
我們可以這樣做——
首先取這個數的下標i,然后取這個數按位數最右邊的1加在i上,所得就是下一個需要加當前數的下標,以此類推,直到取到下標大於等於最大下標n。
代碼——
void Add(int x, int y) { a[x] += y; while(x <= n) { c[x] += y; x += lowbit(x); } }
當然,給某個數加上減上多少也可以用這個做。
區間求和的方法:
取從i到j之間的區間和
一種方法是取從1到j的和,然后減去從1到i的和。
取的方法——
從j開始,然后j減去j按位數最右端的1,得到的就是下一個需要的下標,減到0時結束。
int Sum(int x) { int rt = 0; while(x > 0) { rt += c[x]; x -= lowbit(x); } return rt; } printf("%d\n", Sum(b)-Sum(a-1));
另一種方法是從j開始,如果j減去從右往左第一個1的差小於i,則結果加a[j],然后j -= 1;否則結果加c[j], j減去從右向左第一個1。
int Summ(int l, int r) { int rt = 0; while(r >= l) { if(r-lowbit(r) < l) { rt += a[r]; r -= 1; } else { rt += c[r]; r -= lowbit(r); } } return rt; }
不過這個貌似較慢……
完整代碼——
1 #include <cstdio> 2 #include <cmath> 3 #include <cstring> 4 #include <algorithm> 5 using namespace std; 6 7 const int N = 50005; 8 9 int t, n; 10 int a[N]; 11 int c[N]; 12 char s[10]; 13 int x, y; 14 15 int lowbit(int x) 16 { 17 return x&(-x); 18 } 19 20 void Add(int x, int y) 21 { 22 a[x] += y; 23 while(x <= n) 24 { 25 c[x] += y; 26 x += lowbit(x); 27 } 28 } 29 30 int Sum(int x) 31 { 32 int rt = 0; 33 while(x > 0) 34 { 35 rt += c[x]; 36 x -= lowbit(x); 37 } 38 return rt; 39 } 40 41 int Summ(int l, int r) 42 { 43 int rt = 0; 44 while(r >= l) 45 { 46 if(r-lowbit(r) < l) 47 { 48 rt += a[r]; 49 r -= 1; 50 } 51 else 52 { 53 rt += c[r]; 54 r -= lowbit(r); 55 } 56 } 57 return rt; 58 } 59 60 void Query(int a, int b) 61 { 62 printf("%d\n", Sum(b)-Sum(a-1)); 63 //printf("%d\n", Summ(a, b)); 64 } 65 66 int main() 67 { 68 //freopen("test.in", "r", stdin); 69 scanf("%d", &t); 70 for(int tm = 1; tm <= t; tm++) 71 { 72 scanf("%d", &n); 73 memset(c, 0, sizeof(c)); 74 memset(a, 0, sizeof(a)); 75 int y; 76 for(int i = 1; i <= n; i++) 77 { 78 scanf("%d", &y); 79 Add(i, y); 80 } 81 printf("Case %d:\n", tm); 82 83 scanf("%s", s); 84 while(s[0] != 'E') 85 { 86 scanf("%d%d", &x, &y); 87 if(s[0] == 'A') Add(x, y); 88 if(s[0] == 'Q') Query(x, y); 89 if(s[0] == 'S') Add(x, -y); 90 scanf("%s", s); 91 } 92 } 93 }
