1. 程式人生 > >樹狀陣列-區間查詢+區間修改

樹狀陣列-區間查詢+區間修改

聽說樹狀陣列可以支援區間加??今天特地跑去這裡學習了一下,%%%%%%%%%%%%%,下面結合我的理解再講一講

有關樹狀陣列的基礎知識我就不贅述了,想必大家都明白,如果不清楚可以自己百度,畢竟這不是蒟蒻三言兩語就可以講通的

那現在假設你已經會了樹狀陣列的 “ 單點修改,區間詢問 ” ,我們就來講一講升級版的 “ 區間修改,區間詢問 ”

【寫在前面】

區間修改:

我們讓 sigma (r ,j )表示 r 陣列的前 j 項和,即sigma (r ,j )=r [ 1 ] + r [ 2 ] +r [ 3 ]+……+r [ j ]

用 a 陣列表示原序列,其差分序列為 c 陣列,即 c [ i ]= a [ i ] - a [ i - 1 ] 

顯然,a [ j ] = sigma(c ,j )

然後我們發現若要對序列中的 i ~ j 個數進行操作(比如都加上一個 v ),其實就是 c [ i ] + =  v , c [ j + 1 ] - = v,因為其他在 i ~ j 中間的數由於都加了一個 v ,其對應的 c 陣列不會改變  

【寫在中間】

區間查詢:

首先使用字首和相減這不用多說,那我們就來考慮如何求 1 ~ q 這個區間內各個數的和

ans = a[1] + a[2] + a[3] +……+ a[q-1] + a[q]

       =sigma(c,1) + sigma(c,2) + sigma(c,3) +…… + sigma(c, q-1 ) + sigma(c, q )

       =c[1] + ( c[1] + c[2] ) + ( c[1] + c[2] + c[3] ) + …… + (c[1] + c[2]+……+ c[q-1] )+ ( c[1] + c[2]+……+ c[q] )

      = q*c[1] + (q-1)*c[2] + (q-2)*c[3] +…… + c[q]

     = q* (c[1]+c[2]+...+c[q]) - (0*c[1]+1*c[2]+...+(q-1)*c[q]) 

       A                                                     B

A部分很好辦,就是一般的樹狀陣列求字首和

而對於B部分,我們可以將其看作一個新的陣列c2[i]=(i-1)*c[i],然後照例維護一下即可

每當修改c的時候,就同步修改一下c2,這樣複雜度就不會改變

所以 ans=q*sigma(c,q)-sigma(c2,q)

【寫在最後】

其實任何一道區間加和區間詢問的題都可以交上去試一下

具體程式碼:

#include<cstdio>
#include<cmath>
#include<algorithm>
#define ll long long
#define N 100009
using namespace std;
int lowbits(int x){return x&(-x);}
int n,m;
ll c1[N],c2[N],a[N]; 
void add(ll *t,ll k,int i){
	while(i<=n){	t[i]+=k;	i+=lowbits(i);	}
}
ll querysum(ll *t,int x){
	ll res=0;
	while(x){ res+=t[x];x-=lowbits(x);	}
	return res;
}
int main(){
	scanf("%d%d",&n,&m);
	int i,j,k;
	for(i=1;i<=n;++i){
		scanf("%lld",&a[i]);
		add(c1,a[i]-a[i-1],i);
		add(c2,(i-1)*(a[i]-a[i-1]),i);///////中間不可以寫成(i-1)*c1[i],因為這裡的c1[i]不是我們定義的那個,它是樹狀數組裡的,包含了他前面的值的
	}
	for(i=1;i<=m;++i){
		int ty,x,y;
		ll z;
		scanf("%d%d%d",&ty,&x,&y);
		if(ty==1){
			scanf("%lld",&z);
			add(c1,z,x);add(c1,-z,y+1);
			add(c2,(x-1)*z,x);////和上面同理
			add(c2,-y*z,y+1);
		}
		else{
			ll ans;
			ans=y*querysum(c1,y)-querysum(c2,y);
			ans-=(x-1)*querysum(c1,x-1)-querysum(c2,x-1);
			printf("%lld\n",ans);
		}
	}
	return 0;
}

那這個程式碼和線段樹的一比,你就知道為什麼我要學了,嘿嘿