樹狀數組[區間修改,區間查詢]

也許更好的閱讀體驗

好東西,以後可以不打線段樹了

本篇假定讀者都會最基礎的兩種樹狀數組,即區改單查和單改區查

思考如何維護一個區間的值,想到了差分
對一個差分數組做一次前綴和可以得到每個位置的值
再對每個位置累加一下就是一個區間的值
公式化的講,就是
設差分數組爲cc
則每個位置的值
vali=j=1icjval_i=\sum\limits_{j=1}^ic_j
一個區間[l,r][l,r]的值
sl,r=i=lrvalis_{l,r}=\sum\limits_{i=l}^rval_i
寫成前綴和相減的形式就是
sl,r=i=1rvalii=1l1valis_{l,r}=\sum\limits_{i=1}^rval_i-\sum\limits_{i=1}^{l-1}val_i

不難發現,一個區間的值實際上就是差分數組前綴和的前綴和做減法

也就是說,我們只要維護出前綴和的前綴和就可以用樹狀數組維護區間了
考慮如何維護前綴和的前綴和
sp=i=1pj=1icjs_p=\sum\limits_{i=1}^p\sum\limits_{j=1}^ic_j
考慮每個cjc_j的出現次數,可以得到
sp=i=1p(pi+1)ci=(p+1)i=1pcii=1picis_p=\sum\limits_{i=1}^p\left(p-i+1\right)c_i=\left(p+1\right)\sum\limits_{i=1}^pc_i-\sum\limits_{i=1}^pi *c_i

經過如上的簡單推導,我們只要維護i=1pci\sum\limits_{i=1}^pc_ii=1pici\sum\limits_{i=1}^pi *c_i這兩個東西就可以了
前者就是差分數組,而後者我們只要在維護差分數組時乘以相應的位置的下標即可
這兩個東西我們都可以用樹狀數組維護單點修改區間查詢一樣的方法維護

int c1[maxn],c2[maxn];
int lbt (int x){ return x & -x; }
void modify (int l,int r,int v)//維護差分數組
{
	++r;
	for (int i=l;i<=n;i+=lbt(i))	c1[i]+=v,c2[i]+=l*v;
	for (int i=r;i<=n;i+=lbt(i))	c1[i]-=v,c2[i]-=r*v;
}

最開始這兩句我沒有看懂,不是說好的維護icii*c_i嗎,怎麼寫出來時就變成lvl*vrvr*v
如果你也有這樣的疑惑,這說明太久沒打樹狀數組或者沒想樹狀數組的原理你也忘記樹狀數組的原理了

這個東西維護的是差分數組,而不是差分數組的前綴和!

對於查詢

int query (int l,int r)
{
	int res=0;
	for (int i=r;i;i-=lbt(i))	res+=(r+1)*c1[i]-c2[i];
	for (int i=l-1;i;i-=lbt(i))	res-=l*c1[i]-c2[i];
	return res;
}

如有哪裏講得不是很明白或是有錯誤,歡迎指正
如您喜歡的話不妨點個贊收藏一下吧

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章