樹狀數組最原始的做用就是求前綴和,能夠實現單點修改和區間查詢。html
可是假設如今有:數組
1.區間修改,單點查詢spa
2.區間修改,區間查詢code
可是又不想敲線段樹怎麼辦?htm
就用樹狀數組嘍。blog
假設如今有一個原數組a(假設a[0] = 0),有一個數組d,d[i] = a[i] - a[i-1],那麼博客
a[i] = d[1] + d[2] + .... + d[i]it
d數組就是差分數組class
因此求a[i]就能夠用樹狀數組維護d[i]的前綴和查詢
區間修改,單點查詢:
根據d的定義,對[l,r]區間加上x,那麼a[l]和a[l-1]的差增長了x,a[r+1]與a[r]的差減小了x,因此就對差分數組的前綴和進行修改
設c是差分數組的前綴和
區間修改:
void add(int x,int k) { for (int i = 1;i <= n;i += lowbit(i)) c[i] += k; } { add(l,x); add(r+1,-x); }
單點查詢:
int sum(int x) { int ans = 0; for (int i = x;i > 0;i -= lowbit(i)) ans += c[i]; return ans; }
區間修改,區間查詢:
根據上面的差分數組的定義能夠獲得:
a[1] + a[2] + a[3] + ... + a[k] = d[1] + d[1] + d[2] + d[1] + d[2] + d[3] + ... + d[1] + d[2] + d[3] + ... + d[k]
= Σ(k - i + 1) * d[i] (i從1到k)
變化一下 Σa[i] (i從1到k) = Σ(k+1) * d[i] - i * d[i] (i從1到k)
d[i]能夠用一個前綴和維護,i * d[i]也能夠用一個前綴和進行維護,因此區間修改,區間查詢就變得很方便了
假設c1維護d[i]的前綴和,c2維護d[i] * i的前綴和
區間修改:
void add(int x,int y) { for (int i = x;i <= n;i += lowbit(i)) c1[i] += y,c2[i] += x * y; } { add(l,x); add(r+1,-x); }
區間查詢:
int sum(int x) { int ans1 = 0; int ans2 = 0; for (int i = x;i > 0;i -= lowbit(i)) { ans1 += (x + 1) * c1[i]; ans2 += c2[i]; } return ans1 - ans2; }
比線段樹好寫多了(藍兒仍是容易寫炸
參考瞭如下兩位前輩的博客,感謝:
https://www.cnblogs.com/lcf-2000/p/5866170.html
https://www.cnblogs.com/RabbitHu/p/BIT.html