樹狀陣列-模板
阿新 • • 發佈:2022-05-16
樹狀陣列
目的
是為了優化陣列、字首和陣列,因為這兩個陣列有如下特點:
陣列:
- 單點修改:O(1)
- 區間查詢: O(n)
字首和陣列:
- 單點修改: O(n)
- 區間查詢:O(1)
而當需要同時進行這兩種操作的時候,時間複雜度其實是取決於最壞的情況,即O(n)。
此時需要一種資料結構,能綜合一下這兩種陣列的優點,使不那麼的極端,樹狀陣列就誕生了。
樹狀陣列:
- 單點修改:O(log n)
- 區間查詢:O(log n)
原理
首先,分析一下為什麼陣列、字首和陣列會出現極端的情況。其實陣列相當於是劃分為了[i,i]這樣的n個區間,這有利於單點修改。而字首和陣列是劃分為了(0,i)這樣的n個區間,這有利於區間查詢。
而樹狀陣列是利用二進位制進行了一種新的區間劃分(樹狀陣列的英文名是BIT,二進位制下標樹),使區間劃分的較為勻稱,這樣均衡了其在單點修改與區間查詢之間的能力。
引入幾個概念:
- C(i):區間(i - lowbit(i),i],以i結尾,長度為lowbit(i)的區間的區間和
- lowbit(i):i的二進位制表示中最後一個1及其之後的0(...100000),lowbit(i)可以使用 i & -i輕易求出
- sum(x)求法:將x劃分出的區間進行累和
- add(x,c):將x節點與其父節點都加c,即包含x的區間都加c
其實主要就是一個區間劃分,然後在修改的時候,是要向上找父節點,不斷對父節點進行更新,找負節點的方法是+lowbit(i)。在查詢的時候,是要向下找子區間,不斷的對子區間進行累和,找子區間的方法是-lowbit(i)。
程式碼模板
const int N = 5e5+5; int n; int tr[N],a[N]; int lowbit(int x){ return x & -x; } void add(int x,int c){ for(int i = x;i <= n;i += lowbit(i)) tr[i] += c; } int sum(int x){ int ans = 0; for(int i = x;i;i -= lowbit(i)) ans += tr[i]; return ans; } int query(int l,int r){ return sum(r) - sum(l-1); }