1. 程式人生 > >樹狀陣列區間更新+區間查詢+單點查詢

樹狀陣列區間更新+區間查詢+單點查詢

為了更好地使用複雜度比線段樹更加優化的樹狀陣列,所以必須實現樹狀陣列的區間更新,於是小弟在網上找了找神牛的程式碼,淺淺地分析了分析,但是還遺留了一個問題,所以若有幸有神牛看到此貼,希望解答一下最後的那個問題;

樹狀陣列時間複雜度為O(MlogN), 實際用的時候優於線段樹,且寫得少。

神牛是引入了差分陣列,要維護的差分陣列ks[i] = a[i] - a[i-1]; 可以容易得到a[i] = ks[1] + ks[2] + ... + ks[i]; 即前i項和,為方便記為sigma(ks, i),已經可以看到樹狀陣列的影子了,所以求區間和隨之得到

a[1] + a[2] + .. + a[n] = sigma(ks, 1) + sigma(ks

, 2) + ... + sigma(ks, n);

  = n*ks[1] + (n-1)*ks[2] + ... + 2*ks[n-1] + 1*ks[n];

       =  n*(ks[1] + ks[2] +...+ ks[n]) - (0*ks[1] + 1*ks[2] + ... + (n-1)*ks[n]);

所以可以得到 sum[n] =n * sigma(ks, n)  - (0*ks[1] + 1*ks[2] + ... + (n-1)*ks[n]);

令jk[i] = (i-1) * ks[i];

則 sum[n] = n * sigma(ks, n) - sigma(jk, n);

之後便是構造兩個樹狀陣列;

int lowbit(int k){
	return k & -k;
}
void add(int n, int *c, int k, int va){
	while(k <= n){
		c[k] += va;
		k += lowbit(k);
	}
}

//-------------------------------------

for(i = 1; i <= n; ++i){
		add(n, c1, i, jk[i]-jk[i-1]);
		add(n, c2, i, (i-1)*(jk[i]-jk[i-1]));
	}

然後進行查詢求和
int sigma(int *c, int k){
	int sum = 0;
	while(k){
		sum += c[k];
		k -= lowbit(k);
	}
	return sum;
}
int getSum(int s, int t){
	return (t*sigma(c1, t)-sigma(c2, t)) - ((s-1)*sigma(c1, s-1)-sigma(c2, s-1));
}

進行單點查詢時,只需兩個引數均傳入該點。

在進行區間更新的時候,神牛市通過兩次維護c1,兩次c2得到的,但本人推測了幾種情況,都不能很好的解釋這麼做的原因,

void update(int s, int t, int va){
	add(c1, s, va);
	add(c1, t+1, -va);
	add(c2, s, va*(s-1));
	add(c2, t+1, -va*t);
}

雖然很懸玄,但是精巧的程式碼完美的解決了此類問題,希望有理解為何這麼區間更新的大佬可以留下評論解釋一波~小弟不勝感激。