1. 程式人生 > 其它 >樹狀陣列-模板

樹狀陣列-模板

樹狀陣列

目的

是為了優化陣列、字首和陣列,因為這兩個陣列有如下特點:
陣列:

  • 單點修改: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);
}