線段樹區間更新【Lazy標記】【模版、詳細註釋】【再附上標記永久化模版】
阿新 • • 發佈:2019-02-20
區間更新與點更新不同,它需要更深層次的理解,我講一下最為基本的Add(L, R, V)與Query(L,R)系列,然後以此為基礎可以衍射出其他各種做法:例如Set(L, R, V)系列就是其衍射品,Set系列更改的就是在查詢的時候讀取到Lazy標記就立刻返回,而Add系列不同,他要一直往下讀它的Lazy標記。
首先,建樹就不說了,跟點標記一樣(因為建樹又不用改變什麼)。
先上一下往下更新的Pushdown操作程式碼:
void pushdown(int rt,int l,int r) //非永久性標記,向下遞迴 { if(lazy[rt]) { ll le = (r+l>>1)-l+1; //左子樹長度 ll re = r-l+1-le; //右子樹長度 lazy[rt<<1] += lazy[rt]; lazy[rt<<1|1] += lazy[rt]; sum[rt<<1] += lazy[rt]*le; sum[rt<<1|1] += lazy[rt]*re; lazy[rt] = 0; //清空 } }
查詢:我們如果從根結點開始往下查詢符合區間的點,那麼就要排除那些(ql,qr)區間之外的點,但如果此時的根節點範圍過大,又有Lazy標記,那麼就得同時把Lazy標記往下移動了。
程式碼:
ll query(int l,int r,int rt,int ql,int qr) //查詢函式(求和sum) { if(ql<=l&&qr>=r) return sum[rt]; else { push(rt,l,r); //我們要去處理它的兒子節點了,就需要把Lazy標記往下推了 int mid = l + r>>1; //這裡說明一下:‘+’優先順序比'>>'符號高 ll ret = 0; if(ql<=mid) ret += query(l,mid,rt<<1,ql,qr); if(qr>mid) ret += query(mid+1,r,rt<<1|1,ql,qr); return ret; } }
更新:更新與查詢相似,都是從根往下,若是(ql,qr)包含了這個根所在區間,就直接給這個區間附上Lazy標記並返回,否則就說明有不必要的冗雜的東西,就得把目前這個點的Lazy標記往下移動,然後在新的區間繼續找新的值。
程式碼:
void update(int l,int r,int rt,int ql,int qr,int v) //更新函式 { if(ql<=l&&qr>=r) sum[rt] += (r-l+1)*v,lazy[rt] += v; //此處強調一定要對sum求和別忘記,不然更新父節點會少值 else { pushdown(rt,l,r); //說明這個根結點的範圍有不在(ql, qr)範圍內的,需要遞迴到下一層去尋找範圍內的區間 int mid = l+r>>1; if(ql<=mid) update(l,mid,rt<<1,ql,qr,v); if(qr>mid) update(mid+1,r,rt<<1|1,ql,qr,v); sum[rt] = sum[rt<<1] + sum[rt<<1|1]; } }
最後,附上永久標記話程式碼,我也沒懂不過先做個筆記記下來,總有用到以及學會的一天。
//標記永久化(永久化標記)
void push(int rt,int l,int r) //就是pushudown的作用
{
if(lazy[rt]==0)return;
ll le = (r+l>>1)-l+1;
ll re = r-l+1-le;
lazy[rt<<1] += lazy[rt];
lazy[rt<<1|1] += lazy[rt];
sum[rt<<1] += lazy[rt]*le;
sum[rt<<1|1] += lazy[rt]*re;
lazy[rt] = 0;
}
ll query(int l,int r,int rt,int ql,int qr)
{
if(ql<=l&&qr>=r) return sum[rt];
else
{
//push(rt,l,r);
int mid = l + r>>1;
ll ret = 1ll*(min(qr,r)-max(ql,l)+1) * lazy[rt];
if(ql<=mid) ret += query(l,mid,rt<<1,ql,qr);
if(qr>mid) ret += query(mid+1,r,rt<<1|1,ql,qr);
return ret;
}
}
void update(int l,int r,int rt,int ql,int qr,int v)
{
if(ql<=l&&qr>=r) sum[rt] += 1LL*(r-l+1)*v,lazy[rt] += v;
else
{
int mid = l+r>>1;
if(ql<=mid) update(l,mid,rt<<1,ql,qr,v);
if(qr>mid) update(mid+1,r,rt<<1|1,ql,qr,v);
sum[rt] = sum[rt<<1] + sum[rt<<1|1] + lazy[rt]*(r-l+1);
}
}