樹狀陣列的區間更新和區間查詢
阿新 • • 發佈:2018-11-11
對於區間修改、區間查詢這樣的簡單問題,打一大堆線段樹確實是不划算,所以學習下區間查詢+區間修改的樹狀陣列
設原陣列是a[n],差分陣列c[n],c[i]=a[i]-a[i-1],
那麼明顯地a[i]=sigma (c[i]),如果想要修改a[i]到a[j](比如+v),只需令c[i]+=v,c[j+1]-=v
觀察式子:
a[1]+a[2]+...+a[n]
= (c[1]) + (c[1]+c[2]) + ... + (c[1]+c[2]+...+c[n])
= n*c[1] + (n-1)*c[2] +... +c[n]
= n * (c[1]+c[2]+...+c[n]) - (0*c[1]+1*c[2]+...+(n-1)*c[n]) (式子①)
我們假設 c2[i] = (i-1)*c[i]
每當修改c的時候,就同時修改一下c2,這樣複雜度就不會改變
sigema(a1~an) = n * (c[1]+c[2]+...+c[n]) - (0*c[1]+1*c[2]+...+(n-1)*c[n]) = n*sigma(c[n]) - sigma(c2[n]);
練習題
#include<bits/stdc++.h> using namespace std; int n; int lowbit(int x) { return x&(-x); } void update(long long c[],int x,long long num) { for(int i = x;i <= n;i += lowbit(i)) { c[i] += num; } } long long query(long long c[],int x) { long long sum = 0; for(int i = x;i >= 1;i -= lowbit(i)) { sum += c[i]; } return sum; } long long c[200010]; long long c2[200010]; long long sum[200010]; int main() { int q,op,x,y; long long num; scanf("%d",&n); sum[0] = 0; memset(c2,0,sizeof(c2)); memset(c,0,sizeof(c)); for(int i = 1;i <= n;i ++) { scanf("%lld",&sum[i]); update(c,i,sum[i] - sum[i - 1]); update(c2,i,(i - 1LL)*(sum[i] - sum[i - 1])); } scanf("%d",&q); while(q --) { scanf("%d %d %d",&op,&x,&y); if(op == 1) { scanf("%lld",&num); update(c,x,num); update(c,y+1LL,-num); update(c2,x,(x-1LL)*num); update(c2,y+1LL,-y*num); } else { printf("%lld\n", (y*query(c,y) - query(c2,y) ) - ( (x-1LL) *query(c,x-1LL) -query(c2,x - 1LL) ) ); } } return 0; }