线段树之延时标记(区间修改)及lazy思想
暴力求解
1.最簡(jiǎn)單的方法是:在主函數(shù)中添加一個(gè)循環(huán)?
進(jìn)行 r-l+1次單點(diǎn)修改實(shí)現(xiàn)區(qū)間修改,對(duì)于單個(gè)元素修改時(shí)間復(fù)雜度為 O(log2(n))
所以對(duì)于單個(gè)區(qū)間修改的時(shí)間復(fù)雜度為 O(n*log(n)),甚至比樸素的模擬算法還慢
2.延時(shí)標(biāo)記 lazy tag
延時(shí)標(biāo)記就是在遞歸的過程中,如果當(dāng)前區(qū)間被需要,修改的區(qū)間完全覆蓋,那么就要停止遞歸,并且在上面做一個(gè)標(biāo)記?
3.但是這個(gè)信息沒有更新到每個(gè)元素(即葉子結(jié)點(diǎn)),下次查詢的時(shí)候可能沒法得到足夠的信息。之前在一個(gè)區(qū)間上打了一個(gè)標(biāo)記,這個(gè)標(biāo)記不僅是這個(gè)結(jié)點(diǎn)的性質(zhì),此性質(zhì)作用于整個(gè)子樹中,假設(shè)我們另一個(gè)查詢中包含了,當(dāng)前區(qū)間的子孫區(qū)間,這個(gè)標(biāo)記也要對(duì)之后的查詢產(chǎn)生影響
lazy tag?
1.如果對(duì)線段樹進(jìn)行單點(diǎn)更新,都是在葉子節(jié)點(diǎn)中實(shí)現(xiàn),不會(huì)對(duì)后續(xù)節(jié)點(diǎn)產(chǎn)生影響
2.如果當(dāng)前區(qū)間被需要修改的目標(biāo)區(qū)間完全覆蓋,打一個(gè)標(biāo)記?
如果下一次的查詢或更改包含此區(qū)間,那么將這個(gè)標(biāo)記分解,并傳遞給左右兒子
3.延時(shí)標(biāo)記在需要時(shí),才向下傳遞信息,如果沒有用到,則不再進(jìn)行操作
為了完成這種操作,可以在結(jié)構(gòu)體中增加一個(gè)add數(shù)組存儲(chǔ)區(qū)間的延時(shí)變化量
4.通俗的解釋 lazy的意思,比如現(xiàn)在需要對(duì)[a,b]區(qū)間進(jìn)行加 C操作,
那么就從根節(jié)點(diǎn)[1,n]開始調(diào)用update函數(shù)進(jìn)行操作,如果剛好執(zhí)行到一個(gè)子節(jié)點(diǎn)
它的節(jié)點(diǎn)標(biāo)記為 rt,這時(shí) tree[rt].l==l&&tree[rt].r==r這時(shí)我們可以進(jìn)一步
更新此時(shí)rt節(jié)點(diǎn)的sum[rt]的值,sum[rt]+=(ll)c*(r-l+1);
5.關(guān)鍵的地方:如果此時(shí)按照常規(guī)的線段樹的update操作,這時(shí)候還應(yīng)該更新
rt子節(jié)點(diǎn)的sum[]值,而lazy思想恰恰是暫時(shí)不更新rt子節(jié)點(diǎn)的sum[]值,
到此時(shí)就 return,直到下一次需要用到rt子節(jié)點(diǎn)的值得時(shí)候才去更新,
這樣避免許多無用的操作,從而節(jié)省時(shí)間
6.PushUp(rt);通過當(dāng)前節(jié)點(diǎn)rt把值遞歸向上更新到根節(jié)點(diǎn)
PushDown(rt);通過當(dāng)前節(jié)點(diǎn)rt遞歸向下去更新rt子節(jié)點(diǎn)的值
rt表示當(dāng)前子樹的根,也就是當(dāng)前所在的節(jié)點(diǎn)?
解題模板
1.定義結(jié)構(gòu)體來存儲(chǔ)每個(gè)節(jié)點(diǎn)的子節(jié)點(diǎn)數(shù)值的總和,
add用來記錄該節(jié)點(diǎn)的每個(gè)數(shù)值應(yīng)該加多少
tree[i].l,tree[i].r分別表示某個(gè)節(jié)點(diǎn)的左右區(qū)間,都是閉區(qū)間?
2.PushUp(rt);通過當(dāng)前節(jié)點(diǎn)rt把值遞歸向上更新到根節(jié)點(diǎn)
void PushUp(int rt) {sum[rt] = sum[rt<<1] + sum[rt<<1|1]; }3.PushDown(rt);通過當(dāng)前節(jié)點(diǎn)rt遞歸向下去更新rt子節(jié)點(diǎn)的值
rt表示當(dāng)前子樹的根,也就是當(dāng)前所在的節(jié)點(diǎn)?
void PushDown(int rt,int m) {if(add[rt]){add[rt<<1] += add[rt];add[rt<<1|1] += add[rt];sum[rt<<1] += add[rt] * (m - (m>>1));sum[rt<<1|1] += add[rt] * (m>>1);add[rt] = 0;} }4.建立線段樹
void build(int l,int r,int rt) {tree[rt].l = l;tree[rt].r = r;add[rt] = 0;if(l == r){scanf("%I64d",&sum[rt]);return ;}int m = tree[rt].mid();build(lson);build(rson);PushUp(rt); }5.update函數(shù),lazy思想主要用于這里
通俗的解釋 lazy的意思,比如現(xiàn)在需要對(duì)[a,b]區(qū)間進(jìn)行加 C操作,
那么就從根節(jié)點(diǎn)[1,n]開始調(diào)用update函數(shù)進(jìn)行操作,如果剛好執(zhí)行到一個(gè)子節(jié)點(diǎn)
它的節(jié)點(diǎn)標(biāo)記為 rt,這時(shí) tree[rt].l==l&&tree[rt].r==r這時(shí)我們可以進(jìn)一步
更新此時(shí)rt節(jié)點(diǎn)的sum[rt]的值,sum[rt]+=(ll)c*(r-l+1);
關(guān)鍵的地方:如果此時(shí)按照常規(guī)的線段樹的update操作,這時(shí)候還應(yīng)該更新
rt子節(jié)點(diǎn)的sum[]值,而lazy思想恰恰是暫時(shí)不更新rt子節(jié)點(diǎn)的sum[]值,
到此時(shí)就 return,直到下一次需要用到rt子節(jié)點(diǎn)的值得時(shí)候才去更新,
這樣避免許多無用的操作,從而節(jié)省時(shí)間
6.query函數(shù),也就是用這個(gè)函數(shù)來求區(qū)間和
第一個(gè)if還是區(qū)間的判斷和前面update的一樣,到這里就可以知道答案了,所以就直接return。
接下來的查詢就需要用到rt子節(jié)點(diǎn)的值了,由于我們用了Lazy操作,這段的數(shù)值還沒有更新,因此我們需要調(diào)用PushDown函數(shù)去更新之,滿足if(add[rt])就說明還沒有更新。
__int64 query(int l,int r,int rt) {if(l == tree[rt].l && r == tree[rt].r){return sum[rt];}PushDown(rt,tree[rt].r - tree[rt].l + 1);int m = tree[rt].mid();__int64 res = 0;if(r <= m) res += query(l,r,rt<<1);else if(l > m) res += query(l,r,rt<<1|1);else{res += query(l,m,rt<<1);res += query(m+1,r,rt<<1|1);}return res; }線段樹 區(qū)間更新 區(qū)間查詢模板題??POJ3468
?
總結(jié)
以上是生活随笔為你收集整理的线段树之延时标记(区间修改)及lazy思想的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: poj 1664 放苹果 DPDFS
- 下一篇: 字符检测函数