OI學習筆記之資料結構 樹狀陣列

2021-09-25 15:21:29 字數 3808 閱讀 5317

樹狀陣列複雜度 o(logn).

樹狀陣列是一種維護字首和,區間最大值,區間最小值,區間異或和等滿**換律的東西的資料結構,其支援單點修改和區間查詢。

樹狀陣列其實並不算一棵樹,它是由陣列+二進位制的操作實現的,只是在實現的過程中我們借助了樹形結構的思想,因此樹狀陣列並不需要建樹等操作。

一,認識樹狀陣列

樹狀陣列也是一棵二叉樹,長相類似於一棵偏沉的線段樹,其中最下面一排陣列a代表給定的序列a1,a2…an,c1,c2…為節點編號,其中每個c都維護著若干個連續的a陣列的最大值或和或最小值等,那麼具體每個節點維護幾個a陣列呢?是編號是幾就維護幾個嗎?從圖中來看,顯然不是。

仔細分析樹狀陣列的結構,我們發現c[1],維護的是乙個元素a[1],c[2]維護著兩個元素a[1],a[2],c[4]維護著四個元素a[1],a[2],a[3],a[4]…

c[1] = a[1];

c[2] = a[1] + a[2];

c[3] = a[3];

c[4] = a[1] + a[2] + a[3] + a[4];

c[5] = a[5];

c[6] = a[5] + a[6];

c[7] = a[7];

c[8] = a[1] + a[2] + a[3] + a[4] + a[5] + a[6] + a[7] + a[8];

我們發現每個樹節點維護的節點個數都是2^k 個。那麼如何求到底是多少呢,就是如何求2^k 的值呢,再仔細分析發現,1的二進位制表示是000001,它維護的是2^0 個元素,2的二進位制表示是0000010,它維護的是2^1 個元素…,我們發現對於第i號元素維護的陣列a的個數就是2^k個(其中k等於其二進位制表示末尾零的個數)

那麼如何快速求出2^k的值呢,每次都把它轉化成二進位制再列舉末尾零的個數麼,顯然有點慢,也很麻煩qwq.

為此,我們引入乙個叫做lowbit的神奇操作,使得 第i號元素所維護的個數是lowbit(i)個,即對於第i號元素2k=lowbit(i),(*注意是2k=lowbit(i),不是k=lowbit(i)!)下面我們來詳解一下lowbit;

lowbit(x)= x & (-x) ; //背過就好qwq

這裡利用的負數的儲存特性,負數是以補碼儲存的,對於整數運算 x&(-x)有

當x為0時,即 0 & 0,結果為0;

當x為奇數時,最後乙個位元位為1,取反加1沒有進製,故x和-x除最後一位外前面的位正好相反,按位與結果為0。結果為1。

當x為偶數,且為2的m次方時,x的二進位制表示中只有一位是1(從右往左的第m+1位),其右邊有m位0,故x取反加1後,從右到左第有m個0,第m+1位及其左邊全是1。這樣,x& (-x) 得到的就是x。

當x為偶數,卻不為2的m次方的形式時,可以寫作x= y * (2^k )。其中,y的最低位為1。實際上就是把x用乙個奇數左移k位來表示。這時,x的二進位制表示最右邊有k個0,從右往左第k+1位為1。當對x取反時,最右邊的k位0變成1,第k+1位變為0;再加1,最右邊的k位就又變成了0,第k+1位因為進製的關係變成了1。左邊的位因為沒有進製,正好和x原來對應的位上的值相反。二者按位與,得到:第k+1位上為1,左邊右邊都為0。結果為2^k。

總結一下:x&(-x),當x為0時結果為0;x為奇數時,結果為1;x為偶數時,結果為x中2的最大次方的因子。

//如果看不懂真的背過就好qwq

二,樹狀陣列的基礎操作

1,單點修改:

如果我們想修改數列的某個值怎麼辦呢?比如我們想修改a[1]。

我們再仔細觀察一下這張圖,會發現,如果修改a[1],只會對c[1],c[2],c[4],c[8]產生影響,就是其父節點,父節點的父節點…一直到樹根…,所以我們只需要修改被影響節點的值,其他不被影響的節點的值不需要被修改,但樹狀陣列是一種類似於樹結構的資料結構,真實操作過程中運用了樹的思想,但並沒有建樹等操作。那我們怎麼查詢葉子節點並一路修改至根節點呢,我們發現葉子節點就是我們要修改的a[i],它的第乙個父節點一定是c[i],這個…沒什麼好解釋的,c[i]的父節點是c[i]+lowbit(i),仔細發現下…我們發現每個c節點加上其維護的a節點的數量,就是其父節點的編號,這是樹狀陣列的乙個基本性質,其維護a節點的數量在前文已經說過是lowbit(x)。

code:

inline

void

update

(int k,

int v)

}

2,區間查詢

樹狀陣列也可以支援區間查詢,因為樹狀陣列維護的是字首和,所以在進行區間查詢的時候,比如查詢[x,y]的區間和時,我們要做一次減法,用前y個數的和減去,前x-1個數的和就是[x,y]區間的和,那我們應該怎樣求前y個數的和呢,前y個數的和難道是tree[k]嗎,顯然不是,因為tree[k]維護的是lowbit(k)個數的和,比如tree[3]=a[3],並不等於a[1]+a[2]+a[3]。

再回到這張圖,我們發現前七個數的和等於tree[7]+tree[6]+tree[4],所以我們只要從大到小列舉,每次給x減去lowbit(x)就能算出前x項,不理解可以舉幾個數手推一下

code:

inline

void

getsum

(int x)

}

最後附上樹狀陣列完整**:

#include

#include

#include

#include

using

namespace std;

typedef

long

long ll;

ll tree[

2333

],n,m;

inline

intlowbit

(int x)

inline

void

update

(int x,

int v)

return;}

inline ll getsum

(int x)

return ans;

}int

main()

for(

int i=

1;i<=m;i++

)else

}return0;

}

OI學習筆記 樹狀陣列

花了乙個上午,終於把樹狀陣列弄懂了。打了三種樹狀陣列的模板 樹狀陣列單點更新區間查詢,線段樹區間更新單點查詢,樹狀陣列區間更新區間查詢。第三種太毒了,好久才明白 就是樹一樣的陣列,它的底層實現其實就是乙個陣列,但是我們把它yy成了一棵樹。他的每一列的最頂端有乙個元素,而其他位置都是我們yy出來的,他...

資料結構 樹狀陣列筆記

樹狀陣列 binary indexed tree,bit c i 存放的是在i號之前 包括i號 lowbit i 個整數的和 即 c i 的覆蓋長度是lowbit i 樹狀陣列的下標必須從1開始 int getsum int x void update int x,int v 經典應用 統計序列中在...

資料結構 樹狀陣列 筆記

樹狀陣列的基本用途是 維護序列的字首和。基本思想是 對於給定的序列a,建立乙個陣列c,其中c x 儲存序列a在區間 x lowbit x 1,x 中所有數的和。int lowbit int x 返回lowbit的位數 可以想象乙個樹形結構 如果n不是2的整數次冪,則為森林結構 每個內部節點c x 儲...