前言
對於樹套樹,主席樹等使用到線段樹的比較復雜的數據結構,如果區間修改的話,打標記后pushdown或者pushup是很難做到的完全不行吧
所以這個時候,一個神奇的東西誕生了。。。
正題
線段樹標記永久化,維護一個標記,假設為cov,再維護一個sum
假設修改區間[ql, qr]全部加上v:
和平常一樣,到這個區間后cov[x] += v
但是我們又不想pushup,怎么辦?
很好做,更新的時候每次sum[x] += v * (qr - ql + 1) (注意這里的qr,ql是完全被包含於線段樹[L, R]區間內的)
這個很好理解
void Modify(int x, int l, int r, int ql, int qr, int v){
sum[x] += v * (qr - ql + 1);
if(ql == l && qr == r){ cov[x] += v; return; }
int mid = (l + r) >> 1;
if(qr <= mid) Modify(x << 1, l, mid, ql, qr);
else if(ql > mid) Modify(x << 1 | 1, mid + 1, r, ql, qr);
else Modify(x << 1, l, mid, ql, mid), Modify(x << 1 | 1, mid + 1, r, mid + 1, qr);
}
那么詢問怎么辦
假設我們現在一個區間都打上了標記,表示為這個區間的子區間都需要累加區間修改
換句話說,該區間需要累加某個區間的標記,當且僅當這個區間完全包含該區間,這不就是線段樹詢問時從上往下累加標記就行了嗎?
最后,累加的標記ad * (r - l + 1) + sum[x]就是詢問的答案
int Query(int x, int ad, int l, int r, int ql, int qr){
if(ql == l && qr == r) return sum[x] + ad * (r - l + 1);
int mid = (l + r) >> 1;
if(qr <= mid) return Query(x << 1, ad + cov[x], l, mid, ql, qr);
if(ql > mid) return Query(x << 1 | 1, ad + cov[x], mid + 1, r, ql, qr);
return Query(x << 1, ad + cov[x], l, mid, ql, mid) + Query(x << 1 | 1, ad + cov[x], mid + 1, r, mid + 1, qr);
}
最后
特別鳴謝 Orz Zsy教會我標記永久化