BIT

Binary-Index Tree(Actually not)

什麼是BIT

簡單來說就是"砍一半的線段樹"

 

基本上BIT能做的事情,線段樹都能做

那為什麼還要用BIT?

  1. 實作容易(非常容易,基本上可以一行完成)
  2. 常數小(幾乎是所有\(log\)算法中最快的一種)

 

也就是說,雖然線段樹都能做

但BIT能做得更好

小小的前備知識

取lowbit

定義 \(lowbit(x)\) 為將 \(x\) 以二進位表示下

最右邊的1

例如 \(lowbit(6) = 2\)

\((6)_{10} = (110)_2\) ,因此lowbit為2

例題

\(lowbit (7)\)

\(lowbit(8)\)

\(lowbit(24)\)

Ans點我

\(lowbit((7)_{10}) = lowbit((111)_2) = 1\)

\(lowbit((8)_{10}) = lowbit((1000)_2) = 8\)

\(lowbit((24)_{10}) = lowbit((11000)_2) = 8\)

在C++取lowbit

超級簡單

int lowbit(int x){
    return x & -x;
    //因為C++對負數的處理很神奇
    //所以這樣就可以得到lowbitㄌ
    //我也不知道為什麼
    //別問我咩噗
}

BIT的結構

終於要進重點ㄌ><

This is a BIT(1-base)

看不懂嗎?沒關係,這張圖沒人什麼人看得懂

節點存取這個區間的和

節點在陣列上對應的index

結論?

每個節點(or陣列上的某個位置)

都儲存著某段前綴和

而第\(i\)個節點所儲存的資料為

他的前\(lowbit(i)\)項的和

所以BIT能幹嘛

\(O(log(n))\)單點修改

\(O(log(n))\)區間查詢

Why & How?

回到剛剛那張圖!

單點修改

將第\(3\)項的值增加\(1\)

++bit[3]

++bit[4]

++bit[8]

每次往下一個節點走 \(i += lowbit(i)\)

區間查詢

查詢第\(13\)項的前綴和

bit[13]

bit[8]

bit[12]

每次往下一個節點走 \(i -= lowbit(i)\)

\(query(13) = bit[13] + bit[12] + bit[8]\)

其他操作

只要能查詢前綴和,就能讓兩個前綴和相撿得到區間和

etc: \(query(15) - query(10)\)可以得到11~15的和

 

跟線段樹一樣,只要有結合律的東西,都能放進BIT!

etc: 前綴max,前綴XOR值,前綴gcd

扣的(前綴和BIT)

//1-base prefix sum bit
int bit[(int)1e5+100],n;

int query(int k){
    int ret = 0;
    for (;k>0;k -= k&-k ) ret += bit[k];
    return ret;
}

void modify(int k,int val){
    for (;k<=n;k += k&-k ) bit[k] += val;
}

基本上code簡單到可以背起來XD

應用&例題

再講一些小應用

如果需要後綴而不是前綴呢?

k = n - k (naive)

直接把query跟modify寫反就好了

也就是query變成往大的跑,modify往小的跑

當然,直接在for迴圈前加一行

k = n - k

也是可以啦XD

Just one more

多維BIT!

如果想要取二維的前綴和,也是可以ㄉ

就BIT裡面也是存BIT就好了

複雜度:修改/查詢 \(O(log^2n)\)

 

int bit[1010][1010],n;

int query(int x,int y){
    int val = 0;
    for (int i=x;i;i-=i&-i)
        for (int j=y;j;j-=j&-j)
            val += bit[i][j];
    return val;
}

void modify(int x,int y,int val){
    for (int i=x;i<=n;i+=i&-i)
        for (int j=y;j<=n;j+=j&-j)
            bit[i][j] += val;
}

欸其實還有一個

剛剛提到的BIT是單點修改,區間查詢

但其實BIT也可以做區間修改,單點查詢

可以想一下怎麼做

Hint點我

想一下要怎麼用前綴和表示值

耶終於進例題ㄌ

BITㄉ題目通常都不是難在BIT

是難在你要怎麼想到要用BIT

但如果你BIT中毒就很容易想到ㄛ><

No judge

給定一個大小為\(n\)的序列及\(q\)筆詢問

每筆詢問為

  1. 將第\(i\)項的值改為\(x\)
  2. 問你區間 \([l,r]\) 的和為多少

\(n,q \leq 10^6\)

給定一個序列,求當中有多少個逆序數對

\(n \leq 10^5\)

(這一題有BIT以外的作法ㄛ)

給定一個長度為 \(2n\) 的序列

當中\(1到n\)每個數都會出現恰好2次

定義\(x\)的低窪值為介於\(x\)的兩次出現間

比\(x\)小的數出現了幾次

求所有低窪值的總和

\(n \leq 10^5\)

有一個長寬皆為 \(n\) 的棋盤

有\(q\)筆詢問,每筆詢問可能為

  1. 在 \((x,y)\)上放 \(z\) 顆棋子
  2. 詢問 \((x_1,y_1),(x_2,y_2)\)矩形範圍內有幾顆棋子

\(q \leq 65000\)

\(n \leq 1024, 1 \leq x,y \leq n\)

棋子的和在int範圍內

有\(q\)筆操作

每筆操作可能是在一個multiset中加入一個數\(x\)

或詢問你multiset中的中位數

\(x \leq 10^6\)

\(q \leq 10^6\) in 模競 TL 2s

\(q \leq 10^7\) in tioj TL 5s

給定一個長度為\(n\)的序列\(a\)

問你存在幾組Triple \((i,j,k)\)

\(s.t.  i < j < k 且 a_i > a_j >a_k \cup a_i < a_j < a_k\)

\(n \leq 50000\),最多20筆測資

更多例題?

詳見FHVirus的BIT簡報

ㄛ不過後面的題目超難

我都還沒寫出來咩噗

BIT / Fenwick Tree

By yungyao

BIT / Fenwick Tree

BIT (Binary Indexed Tree)

  • 325