樹狀陣列 區間修改
愚蠢的我花了好久好久才看明白他的意思….
最近不是在弄樹狀陣列嘛,然後這個區間修改,怎麼也想不明白,今天看了一下午部落格,總算弄懂了.
讓我們開始.我會盡量使用常數而不是變數,為了讓你完全清楚.
如果出現公式,都不難,如果你一時看不清,可以拿筆寫一寫,保證沒有任何難度.
我們約定,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;
}