1. 程式人生 > >線段樹區間更新【Lazy標記】【模版、詳細註釋】【再附上標記永久化模版】

線段樹區間更新【Lazy標記】【模版、詳細註釋】【再附上標記永久化模版】

  區間更新與點更新不同,它需要更深層次的理解,我講一下最為基本的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);
    }
}