區間修改區間求和的樹狀數組
阿新 • • 發佈:2018-05-06
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; }
區間修改區間求和的樹狀數組