1. 程式人生 > >區間修改區間求和的樹狀數組

區間修改區間求和的樹狀數組

http col 化簡 can lld font color int www.

好像樹狀數組雖然常數小,編程簡單,可是資瓷的操作有限,

普通的樹狀數組只資瓷單點修改和區間查詢,首先要將其升級為區間修改

我們利用差分來進行

定義差分數組b[i]=a[i]-a[i-1]

這樣$ a[j]=\sum_{i=1}^jb[i] $

這樣我們只要用樹狀數組維護一下b[i]的前綴和就好了

修改區間[l,r]時我們只需要add(l,x)和add(r+1,-x)就好了

題目鏈接

就是一個樹狀數組資瓷區間修改和單點查詢的模板

但是這還不夠

我們還要區間求和

我們來看

$ \sum_{i=1}^ja[i]=\sum_{i=1}^j\sum_{k=1}^ib[k] $

我們將上面的式子可以寫成

$ \sum_{i=1}^ja[i]=\sum_{i=1}^jb[i]*(j-i+1) $

但是上面的式子還是不好維護

我們將其化簡為

$ \sum_{i=1}^ja[i]=j*\sum_{i=1}^jb[i]-\sum_{i=1}^jb[i]*(i-1) $(自己推一下,驗證一下)

這樣我們就可以利用兩個樹狀數組完成區間修改,查詢的操作

用兩顆樹狀數組A,B,A維護b[i],B維護b[i]*(i-1)

我們可以很容易得到查詢結果就是A(r)*r-B(r)-A(l-1)*(l-1)+B(l-1)

關於修改操作

我們按之前的討論維護即可

附上代碼

題目鏈接

# include<iostream>
# include
<cstdio> # include<cmath> # include<algorithm> const int mn = 100005; int n,m; struct Binary_tree{ long long tr[mn]; void add(int i,long long x) { while(i<=n) { tr[i]+=x; i+=i&-i; } } long long qsum(int i) { long long ret=0; while
(i>0) { ret+=tr[i]; i-=i&-i; } return ret; } }A,B; long long a[mn]; int main() { int opt,x,y,z; scanf("%d%d",&n,&m); for(int i=1;i<=n;i++) { scanf("%lld",&a[i]); } for(int i=1;i<=n;i++) { A.add(i,a[i]-a[i-1]); B.add(i,(a[i]-a[i-1])*(i-1)); } for(int i=1;i<=m;i++) { scanf("%d%d%d",&opt,&x,&y); if(opt==1) { scanf("%d",&z); A.add(x,z); A.add(y+1,-z); B.add(x,z*(x-1)); B.add(y+1,-z*y); } else { printf("%lld\n",(A.qsum(y)*y-(x-1)*A.qsum(x-1))-B.qsum(y)+B.qsum(x-1)); } } return 0; }

區間修改區間求和的樹狀數組