1. 程式人生 > >樹狀陣列 區間修改

樹狀陣列 區間修改

愚蠢的我花了好久好久才看明白他的意思….
最近不是在弄樹狀陣列嘛,然後這個區間修改,怎麼也想不明白,今天看了一下午部落格,總算弄懂了.
讓我們開始.我會盡量使用常數而不是變數,為了讓你完全清楚.
如果出現公式,都不難,如果你一時看不清,可以拿筆寫一寫,保證沒有任何難度.
我們約定,a[i]代表資料本身,(為了和陣列陣列中的資料區別)
首先,我們要實現區間修改,那麼我們就不能和以前一樣,我們假設一個c陣列,這裡面c[i],代表著a[i]-a[i-1].

問題來到,為什麼我們要構造c[i].
想一想,假如我要對a[l]+…+a[r]進行區間修改,怎麼辦?
首先,c[i],是a[i]陣列的一個對映,也就是說,當我們使用a[i]來建立樹狀陣列時,a[i]表現的是每個節點的數字本身,而我們的c[i],則是表現每個節點的數字之間的差.那麼我們可以想象,假如a[l]~a[r],每個都加x,那麼在用a[i]表示節點數字性質時,我們理所當然的就是每個都加x,但是我們現在用c[i]表示節點數字性質時,你會發現,除了c[l]會加x,c[r+1]會減x外,其他地方,都沒有變化.這便是關鍵了.
結論,利用c[i]作為a[i]的對映,我們可以通過兩次修改,達到原來的n次修改的效果.那麼接下來,就是我們區間修改的具體實現了.

可以認為接下來的一切,並沒有顯著的邏輯,他只是就是這樣而已.

當我們有了這個定義之後,顯然的,a[2] = c[2]+c[1],a[3] = c[3]+c[2]+c[1].那麼這時候,我們想要計算a[1]+a[2]+….+a[n],也就是
c1 + (c2+c1)+(c3+c2+c1)+(…)+(Cn+Cn-1+…+C1),對吧?

我們可以進行整合,將他變為
n*(c1+c2+c3+…+cn)-(0*c1+1*c2+2*c3+…+(n-1)*Cn).
為什麼這裡關鍵?你觀察會發現,這是兩個字首和的形式,一個是c1+c2+..+cn,一個是(1-0)*c1+(2-1)*c2+…+(n-1)*Cn.
那麼說道字首和,我們樹狀陣列要上場了!
我們不妨假設d[i]= (i-1)*c[i].
顯然,我們維護一個c[i]陣列對應的樹狀陣列(注意他們兩個不是一個東西),就可以很快的對c[i]進行求和,同時,建立c[i]的對映,d[i]的樹狀陣列,也可以很快求出他的字首和了.

/*  xzppp  */
#include <iostream>
#include <vector>
#include <cstdio>
#include <string.h>
#include <algorithm>
#include <queue>
#include <map>
#include <string>
#include <cmath>
#include <bitset>
#include <iomanip>
using namespace
std; #define FFF freopen("in.txt","r",stdin);freopen("out.txt","w",stdout); #define lson l,m,rt<<1 #define rson m+1,r,rt<<1|1 #define MP make_pair #define PB push_back typedef long long LL; typedef unsigned long long ULL; typedef pair<int,int > pii; typedef pair<double,double > pdd; typedef pair<double,int > pdi; const int MAXN = 200000+17; const int MAXM = 20; const int MAXV = 2*1e3+17; const int INF = 0x7fffffff; const int MOD = 1e9+7; LL bit[2][MAXN],n,a[MAXN]; void add(int k,int p,LL x) { while(p<=n) { bit[k][p] += x; p += p&-p; } } LL sum(int k,int p) { LL res = 0; while(p>0) { res += bit[k][p]; p -= p&-p; } return res; } void additv(int from,int to,LL x) { add(0,from,x); add(0,to+1,-x); add(1,from,(from-1)*x); add(1,to+1,(to)*(-x)); } LL sumitv(int from,int to) { LL res = 0; res += to*sum(0,to); res -= sum(1,to); res -= (from-1)*sum(0,from-1); res += sum(1,from-1); return res; } int main() { #ifndef ONLINE_JUDGE FFF #endif int m; cin>>n; for (int i = 1; i <= n; ++i) { scanf("%lld",a+i); add(0,i,a[i]-a[i-1]); add(1,i,(i-1)*(a[i]-a[i-1])); } cin>>m; for (int i = 0; i < m; ++i) { LL cmd,a,b,c; scanf("%lld",&cmd); if(cmd==1) { scanf("%lld%lld%lld",&a,&b,&c); additv(a,b,c); } else { scanf("%lld%lld",&a,&b); printf("%lld\n",sumitv(a,b)); } } return 0; }