Binary-Index Tree(Actually not)
簡單來說就是"砍一半的線段樹"
基本上BIT能做的事情,線段樹都能做
也就是說,雖然線段樹都能做
但BIT能做得更好
定義 \(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\)
超級簡單
int lowbit(int x){
return x & -x;
//因為C++對負數的處理很神奇
//所以這樣就可以得到lowbitㄌ
//我也不知道為什麼
//別問我咩噗
}
看不懂嗎?沒關係,這張圖沒人什麼人看得懂
節點存取這個區間的和
節點在陣列上對應的index
每個節點(or陣列上的某個位置)
都儲存著某段前綴和
而第\(i\)個節點所儲存的資料為
他的前\(lowbit(i)\)項的和
\(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
//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
多維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中毒就很容易想到ㄛ><
給定一個大小為\(n\)的序列及\(q\)筆詢問
每筆詢問為
\(n,q \leq 10^6\)
給定一個序列,求當中有多少個逆序數對
\(n \leq 10^5\)
(這一題有BIT以外的作法ㄛ)
給定一個長度為 \(2n\) 的序列
當中\(1到n\)每個數都會出現恰好2次
定義\(x\)的低窪值為介於\(x\)的兩次出現間
比\(x\)小的數出現了幾次
求所有低窪值的總和
\(n \leq 10^5\)
有一個長寬皆為 \(n\) 的棋盤
有\(q\)筆詢問,每筆詢問可能為
\(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筆測資