線段樹區間更新操作及Lazy思想(詳解)
此題題意很好懂:
給你N個數,Q個操作,操作有兩種,‘Q a b ’是詢問a~b這段數的和,‘C a b c’是把a~b這段數都加上c。
需要用到線段樹的,update:成段增減,query:區間求和
介紹Lazy思想:lazy-tag思想,記錄每一個線段樹節點的變化值,當這部分線段的一致性被破壞我們就將這個變化值傳遞給子區間,大大增加了線段樹的效率。
在此通俗的解釋我理解的Lazy意思,比如現在需要對[a,b]區間值進行加c操作,那麽就從根節點[1,n]開始調用update函數進行操作,如果剛好執行到一個子節點,它的節點標記為rt,這時tree[rt].l== a && tree[rt].r == b 這時我們可以一步更新此時rt節點的sum[rt]的值,sum[rt] += c* (tree[rt].r - tree[rt].l + 1),註意關鍵的時刻來了,如果此時按照常規的線段樹的update操作,這時候還應該更新rt子節點的sum[]值,而Lazy思想恰恰是暫時不更新rt子節點的sum[]值,到此就return,直到下次需要用到rt子節點的值的時候才去更新,這樣避免許多可能無用的操作,從而節省時間。
下面通過具體的代碼來說明之。
在此先介紹下代碼中的函數說明:
1 2 |
<span class = "com" >#define</span><span class = "pln" > lson l</span><span class = "pun" >,</span><span class = "pln" >m</span><span class = "pun" >,</span><span class = "pln" >rt</span><span class = "pun" ><<</span><span class = "lit" >1</span><span class = "pln" >
</span><span class = "com" >#define</span><span class = "pln" > rson m</span><span class = "pun" >+</span><span class = "lit" >1</span><span class = "pun" >,</span><span class = "pln" >r</span><span class = "pun" >,</span><span class = "pln" >rt</span><span class = "pun" ><<</span><span class = "lit" >1</span><span class = "pun" >|</span><span class = "lit" >1</span>
|
宏定義左兒子lson和右兒子rson,貌似用宏的速度要慢。
PushUp(rt):通過當前節點rt把值遞歸向上更新到根節點
PushDown(rt):通過當前節點rt遞歸向下去更新rt子節點的值
rt表示當前子樹的根(root),也就是當前所在的結點
1 __int64 sum[N<<2],add[N<<2];
2 struct Node
3 {
4 int l,r;
5 int mid()
6 {
7 return (l+r)>>1;
8 }
9 } tree[N<<2];
這裏定義數據結構sum用來存儲每個節點的子節點數值的總和,add用來記錄該節點的每個數值應該加多少
tree[].l tree[].r分別表示某個節點的左右區間,這裏的區間是閉區間
下面直接來介紹update函數,Lazy操作主要就是用在這裏
1 void update(int c,int l,int r,int rt)//表示對區間[l,r]內的每個數均加c,rt是根節點
2 {
3 if(tree[rt].l == l && r == tree[rt].r)
4 {
5 add[rt] += c;
6 sum[rt] += (__int64)c * (r-l+1);
7 return;
8 }
9 if(tree[rt].l == tree[rt].r) return;
10 PushDown(rt,tree[rt].r - tree[rt].l + 1);
11 int m = tree[rt].mid();
12 if(r <= m) update(c,l,r,rt<<1);
13 else if(l > m) update(c,l,r,rt<<1|1);
14 else
15 {
16 update(c,l,m,rt<<1);
17 update(c,m+1,r,rt<<1|1);
18 }