【初識——樹狀數組】 區間求和


說樹狀數組其實是一個索引表,但是是一個特殊的,樹狀的索引表,它利用了二進制的一些特性。

 

就區間求和的要求來說:

首先我們用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);
    }
}

 

 

當然,給某個數加上減上多少也可以用這個做。

 

區間求和的方法:

取從ij之間的區間和

一種方法是取從1j的和,然后減去從1i的和。

取的方法——

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 }
View Code

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM