1. 程式人生 > >樹狀數組學習

樹狀數組學習

get 其中 return 更新 數字 src blog 思想 update

樹狀數組的功能和線段樹一樣。但是,這個東西是真的好寫@。@

學習的博客:樹狀數組

  樹狀數組主要的話可以實現三個功能①單點修改,區間查詢②區間修改,單點查詢.3、區間修改,區間查詢。樹狀數組和線段樹思想有點像,就是通過某個點的值來代替區間值,實現區間的運算。

技術分享圖片

  首先,一個很重要的操作是(x&-x)這個式子的結果是"從低位向高位數,第一個數字1"。我們將(x&-x)作為x管理的區間長度,如圖所示。這個時候,我們發現x+(x&-x)的這個值所管理的區間也會到x,那麽這樣遞歸下去就可以將所有管理x的區間都改變值(改變操作)。求和時則不斷向下遞歸,就能求出區間和。//接下來代碼要不要加上原數組的值要視情況而定。

 1 void add(int x,int num)
 2 {
 3     for(;x<=MAX_N;x+=(x&-x))
 4         tree[x] += num;
 5 }
 6 int sum(int x)
 7 {
 8     int s = 0;
 9     for(;x>0;x-=(x&-x))
10         s += tree[x];
11     return s;
12 }

1、單點修改,區間查詢:

  每次改變點的值,再sum減一下就好了。sum(a)-sum[b-1]。(是b-1,因為求得是閉區間的和)

關於後兩種方法,另一個博客寫的比較清晰:樹狀數組2

2、區間修改,單點查詢:(註意這裏只能單點查詢)

  思想其實是化區間修改為點修改(這裏的思想有點像是求前綴和)。要在(l,r)區間+k,則在l位置+k表示l及後面的位置都加了k,再在r+1的位置-k,表示從r+1開始的位置都減了k,這樣其實就實現了區間的修改。把區間[L,R]的數都加上X的操作就變成了兩個點修改:B[L]+=X 和 B[R+1]-=X; //這裏是簡寫,還需要更新它們的父節點。(sum[x]);

3、區間修改,區間查詢:(引用別人吧@。@)

  首先引入delta數組 delta[i]表示區間 [i, n] 的共同增量 於是修改區間 [l, r] 時修改 delta[l] 和 delta[r + 1] 即可(就是差分的思路)。查詢的時候是查詢區間 [l, r] 的和 即sum[r] - sum[l - 1] 所以現在的問題是求sum[i]。

1 sum[i] = a[1]+...+a[i] + delta[1]*i + delta[2]*(i - 1) + delta[3]*(i - 2)+...+delta[i]*1   // a[i]為原始數組
2        = presum( a[x] ) + presum( delta[x]  *  (i + 1 - x) )
3        = presum( a[x] ) + (i + 1) * presum( delta[x] ) - presum( delta[x] * x )

其中 sigma( a[x] ) 是可以預處理出來的 於是只需要維護 delta[x] 與 delta[x] * x 的前綴和(作為兩個樹狀數組就可以了)

用兩個樹狀數組,分別叫做d和s,d維護差分,s維護x*d[x]。

①進行區間修改操作時:

update(d,l,x);update(d,r+1,-x);

update(s,l,x*l);update(s,r+1,-x*(r+1));

②進行區間查詢操作時:

sum(L,R)=sum(1,R)-sum(1,L-1)

sum(1,L-1)=L*query(d,L-1)-query(s,L-1)

sum(1,R)=(R+1)*query(d,R)-query(s,R)

 

樹狀數組學習