數據結構-樹狀數組(一)
阿新 • • 發佈:2018-11-14
stream owb 數組長度 using lan alt 存儲 單點 info
例題:P3374
復習筆記:樹狀數組(一)
基本原理
樹狀數組,顧名思義,是一個存儲方式像樹一樣的數組。它只需要開和原數組一樣大小的內存,但是每個數的位置存的並不是每個數的原始值,而是像這樣:
(引用自度娘)
或者用數據來說,假設原數組為A[N],樹狀數組為C[N],那麽存儲方式就像下面這樣
C1 = A1 C2 = A1 + A2 C3 = A3 C4 = A1 + A2 + A3 + A4 C5 = A5 C6 = A5 + A6 C7 = A7 C8 = A1 + A2 + A3 + A4 + A5 + A6 + A7 + A8 ... C16 = A1 + A2 + A3 + A4 + A5 + A6 + A7 + A8 + A9 + A10 + A11 + A12 + A13 + A14 + A15 + A16 這就是樹狀數組的存儲方式。樹狀數組的優勢
可以很簡單實現區間修改單點查詢、區間查詢單點修改、
例:輸出數組x到y的區間和
暴力模擬:
1 int sum = 0; 2 for(int i = x;i <= y;i++) 3 sum += a[i];
很明顯時間復雜度是O(n),當數據量大並且需要多次查詢,難免TLE,樹狀數組可以解決這個問題
程序實現
性質:
設節點編號為x,那麽這個節點管轄的區間為2^k(其中k為x二進制末尾0的個數)個元素。因為這個區間最後一個元素必然為Ax,所以很明顯:Cn = A(n – 2^k + 1) + ... + An。 所以,當需要修改第x個數時,需要把所有管轄x的節點進行修改。這裏就需要用到了一個很巧妙的節點轉移量:Lowbit。lowbit(x)=x&(-x)。所以程序實現是這樣節點轉移函數:lowbit
1 int lowbit(int x){ 2 return x&(-x); 3 }
甚至可以是這樣
1 #define lowbit(x) x&-x
單點修改
在數組長度n的範圍內,從x開始,每次修改完,下一個要修改的數就是當前修改數的lowbit值,這樣以此類推,就可以把管轄著x的點全部修改完,也就是這樣
1 void add(int x,int k){ 2 while(x <= n){ 3 tree[x] += k; 4 x += lowbit(x) 5 }
這就是把第x個點加上k的操作(tree為樹狀數組)
區間查詢
根據樹狀數組的存儲方式,將單點修改的程序逆過來,也就是在>=1的範圍內,從x開始每次減去當前下標lowbit,並將節點權加入到總和,就可以求出A1+A2...+Ax(A為原數組值),也就是第x個數的前綴和。
1 int sum(int x) 2 { 3 int ans = 0; 4 while(x != 0) 5 { 6 ans += tree[x]; 7 x -= lowbit(x); 8 } 9 return ans; 10 }
那麽想要求出x到y的區間和,只需要用Ay的前綴和減去Ax-1的前綴和即可,就像這樣
1 int search(int x,int y){ 2 return sum(y) - sum(x - 1); 3 }
但大多數情況下不需要寫函數,直接寫到主程序裏。
例題:P3374
大意:實現區間查詢,單點修改
第一行包含兩個整數N、M,分別表示該數列數字的個數和操作的總個數。
第二行包含N個用空格分隔的整數,其中第i個數字表示數列第i項的初始值。
接下來M行每行包含3個整數,表示一個操作,具體如下:
操作1: 格式:1 x k 含義:將第x個數加上k
操作2: 格式:2 x y 含義:輸出區間[x,y]內每個數的和
程序就是這樣:
1 #include <iostream> 2 #include <cstdio> 3 #include <algorithm> 4 #include <cmath> 5 #include <cstring> 6 using namespace std; 7 int n,m,tree[2000010]; 8 int lowbit(int k) 9 { 10 return k & -k; 11 } 12 void add(int x,int k) 13 { 14 while(x<=n) 15 { 16 tree[x]+=k; 17 x+=lowbit(x); 18 } 19 } 20 int sum(int x) 21 { 22 int ans=0; 23 while(x!=0) 24 { 25 ans+=tree[x]; 26 x-=lowbit(x); 27 } 28 return ans; 29 } 30 int main() 31 { 32 cin>>n>>m; 33 for(int i=1;i<=n;i++) 34 { 35 int a; 36 scanf("%d",&a); 37 add(i,a); 38 } 39 for(int i=1;i<=m;i++) 40 { 41 int a,b,c; 42 scanf("%d%d%d",&a,&b,&c); 43 if(a==1) 44 add(b,c); 45 if(a==2) 46 cout<<sum(c)-sum(b-1)<<endl; 47 } 48 }
數據結構-樹狀數組(一)