樹狀陣列學習小結

2021-06-27 04:24:59 字數 4474 閱讀 7790

樹狀陣列,又稱二進位制索引樹,英文名binary indexed tree。

一、樹狀陣列的用途

主要用來求解數列的字首和,a[0]+a[1]+...+a[n]。

由此引申出三模擬較常見問題:

1、單點更新,區間求值。(hdu1166)

2、區間更新,單點求值。(hdu1556)

3、求逆序對。(hdu2838)

二、樹狀陣列的表示

1、公式表示

設a為乙個已知的數列。c為樹狀陣列。則會有

c[i]=a[j]+...+a[i];j=i&(-i)=i&(i^(i-1))。

2、圖形表示

(注:1、最下面的一行表示陣列a,上面的二進位制表示的部分是c;

2、**於

從以上可以發現:

1、樹狀陣列c是表示普通陣列a的一部分的和。

2、小標為奇數時,c[i]只能管轄乙個a[i]。

3、c[i]的最後乙個數一定是a[i]。

三、樹狀陣列的關鍵**

1、[cpp]view plain

copy

print?

intlowbit(

intx)    

這段**可以簡單的理解為是樹狀陣列向前或向後衍生是用的。

向後主要是為了找到目前節點的父節點,比如要將c[4]+1,那麼4+(4&(-4))=8,c[8]+1,8+(8&(-8))=16,

c[16]+1。

向前主要是為了求字首和,比如要求a[1]+...+a[12]。那麼,c[12]=a[9]+...+a[12];然後12-12&(-12)=8,

c[8]=a[1]+...+a[8]。

2、[cpp]view plain

copy

print?

void

modify(

intpos,

intnum)  

//pos為陣列下標位置,num為要增加的值 

}  這段**是用來更新樹狀陣列的,包括區間更新、單點更新。

就是想剛才所說的,一點更新了,要不斷將父節點也更新。

3、[cpp]view plain

copy

print?

intgetresult(

intpos)  

//求a[1]+...+a[pos] 

return

sum;   

}                  

這段**用來求解字首和的。

就像剛才說的,求解a[1]+...+a[12],也就是c[12]+c[8]搞定。

四、樹狀陣列的優點

1、原本的長度為n的數列求和時間複雜度為o(n),更改的時間複雜度為o(1)。

樹狀陣列將其優化為o(logn)。在n較大時,效率更高。

2、樹狀陣列編碼簡單。

五、注意

1、樹狀陣列的下標要從1開始。

2、在學習的過程中遇到這麼個問題。不知道為什麼pos+pos&(-pos)就到了pos的父節點,也不知道

為什麼pos-pos&(-pos)就得到了下乙個無聯絡的節點,從而可以得到字首和。

我只能說:我不懂如何證明,這是數學問題了,樹狀陣列的發明者應該就是發現了這點才搞出樹狀

陣列的吧。初學者不妨拋開這點,專注於事實,將上面的圖形自己計算畫一遍,非常有利於理解。

六、符**:

hdu1166

單點更新,區間求值

[cpp]view plain

copy

print?

#include

using

namespace

std;  

const

intmaxn=50001;  

inta[maxn];  

intc[maxn];  

intn;   

intlowbit(

intt)  

void

modify(

intt,

intnum)  

}  intgetresult(

intt)  

return

num;   

}  void

init()  

}  intmain()  

}  }  system("pause"

);  

return

0;  

}   

hdu1556

區間更新,單點求值

[cpp]view plain

copy

print?

#include

#include

using

namespace

std;  

const

intmaxn=100001;  

intc[maxn];  

intn;  

intlowbit(

intt)  

void

insert(

intt,

intd)  

}  intgetsum(

intt)  

return

sum;  

}  int

main()  

for(int

j=1;j

printf("%d\n"

,getsum(n));  

}  system("pause"

);  

return

0;  

}  

hdu2838

求逆序對

[cpp]view plain

copy

print?

#include

#include

using

namespace

std;  

const

intmaxn=100001;  

struct

node  

tree[maxn];           

intn;  

intlowbit(

intx)  

void

modify(

intx,

inty,

intt)  

}  __int64

query_cnt(

intx)   

//比x小的數的個數 

return

sum;  

}  __int64

query_sum(

intx)  

//比x小的所有數之和 

return

sum;  

}  int

main()  

}  printf("%i64d\n"

,ans);   

}  system("pause"

);  

return

0;  

}   

七、二維樹狀陣列

c[x][y]=sum(a[i][j])。其中,x-lowbit(x)+1<=i<=x,y-lowbit(y)+1<=j<=y。

例題:hdu1892

二維樹狀陣列一般就是對矩陣的操作,更新、求值。。。

**:[cpp]view plain

copy

print?

#include

#include

using

namespace

std;  

const

intmaxn=1005;  

intc[maxn][maxn];  

intlowbit(

intx)  

void

modify(

intx,

inty,

intval)  

}  }  int

getresult(

intx,

inty)  

}  return

sum;  

}  int

getval(

intx,

inty)  

void

init()  

}  }  int

main()  

case

'a':  

case

'm':  

case

'd':  

}  }  }  

system("pause"

);  

return

0;  

}  

八、參考文章

樹狀陣列小結

最普通的樹狀陣列 實現用low bi tlowbit lowbit 的優秀性質 原理很簡單 pragma gcc optimize o3 pragma g optimize o3 include include include define maxn 500005 define ll long lo...

Book 樹狀陣列 小結

差不多花了10天學樹狀陣列,是照著這篇部落格做的題目,還差幾道 1.幾個注意的地方 1 lowbit 0 0 會無限迴圈,會導致tle掉,給輸入進去的x 一下就好 2 當給的x的範圍很大的時候,注意要離散化 2.然後就是兩個基本的操作 1 add 2 sum 3.複雜度是log n 4.現在做到的題...

樹狀陣列學習

之前寫的題也遇到過用樹狀陣列,當時都是現查現學,而且總是搞不懂,今天又遇到了一道求區間和的題,不管最後是不是用樹狀陣列可以a,但是既然已經想到了這,就打算好好學習一下。可惜之前查到的資料都沒有儲存記錄,所以又重新查了些資料,彙總學習如下 文末附上樹狀陣列的詳細 樹狀陣列主要用到的操作 int low...