1. 程式人生 > 實用技巧 >論打含有Pushdown線段樹的正確姿勢

論打含有Pushdown線段樹的正確姿勢

最近線段樹打的我非常的難受,特別是含有Pushdown的那種,打一個錯一個,還不如打暴力拿一些基礎分。所以說我特意請教了大佬,然後我發現其實自己的線段樹一直都是有一些問題的,下面我就來介紹一下如何用正確的姿勢來打線段樹保證以後打線段樹都不會錯!!!

首先為了不打錯,我們首先要對Pushdown這個東西正確的理解。

Pushdown

先宣告一下,Pushdown這個操作基本上只有在區間修改上產生作用,只要我們是區間修改,我們不可能單個單個點的改,這樣的話,複雜度比寫暴力還大!!!!所以說我們的想法就是在我們找到的要改的一整個區間上加上一個標記,Lazytag,然後在後續我們要查詢比該區間小的區間的時候我們順勢把Lazytag下傳下去,這樣我們就可以達到目的了。而Pushdown正好就是幹這件事情的。

那麼我們Pushdown的時候要注意一些什麼呢?

  • 首先我們要注意的就是不能引起下傳混亂,比如說我們一個節點被打上了標記並被修改了,然後後續再Pushdown的時候,該標記因為還在該節點上,然後該節點就又被修改了一次,然後標記才被下傳。

為了避免上面這個情況,我們約定我們的Lazytag標記是給該節點的兒子們看的,而該節點不用管,因為,我們又規定,每次當我們modify的時候找到了我們要修改的區間,那麼我們就直接更改當前區間的資訊,然後再為改區間打上標記。

所以說有了上面的這些約定,我們在Pushdown中乾的事情也就非常的清晰並且不混亂了!!!我們每次只需要下傳當前父親節點的標記,然後修改兒子所在的當前區間即可!!

P.s:修改區間函式可以重新寫一個add函式,這樣可以省去非常多的程式碼,並且使整篇程式碼顯得非常的清晰。

下面給出用該方法寫的luogu的兩個模板線段樹程式碼,比以前寫的要清真太多了!!!

題目:P3372 【模板】線段樹 1

#include<bits/stdc++.h>
#define LL long long
#define N 100005
using namespace std;
LL root,m,n,cnt=0,ini[N];
struct sd{
    LL son[2],l,r,sum,add;
}node[N*3];
void update(LL k){node[k].sum=node[node[k].son[0]].sum+node[node[k].son[1]].sum;}
void add(LL k,LL val)
{
    node[k].add+=val;
    node[k].sum+=val*(node[k].r-node[k].l+1);
}
void pushdown(LL k)
{
    add(node[k].son[0],node[k].add);
    add(node[k].son[1],node[k].add);
    node[k].add=0;
}
void Buildtree(LL &k,LL l,LL r)
{
    cnt++;k=cnt;node[k].l=l;node[k].r=r;
    if(l==r)node[k].sum=ini[l];
    else
    {
        LL mid=(l+r)/2;
        Buildtree(node[k].son[0],l,mid);
        Buildtree(node[k].son[1],mid+1,r);
        update(k);
    }
}
void modify(LL k,LL l,LL r,LL val)
{
    if(node[k].l==l&&node[k].r==r) add(k,val);
    else
    {
        pushdown(k);
        LL mid=(node[k].l+node[k].r)/2;
        if(r<=mid) modify(node[k].son[0],l,r,val);
        else if(l>mid) modify(node[k].son[1],l,r,val);
        else modify(node[k].son[0],l,mid,val),modify(node[k].son[1],mid+1,r,val);
        update(k);
    }
}
LL query(LL k,LL l,LL r)
{
    if(node[k].l==l&&node[k].r==r) return node[k].sum;
    else
    {
        pushdown(k);
        LL mid=(node[k].r+node[k].l)/2;
        if(r<=mid) return query(node[k].son[0],l,r);
        else if(l>mid) return query(node[k].son[1],l,r);
        else return query(node[k].son[0],l,mid)+query(node[k].son[1],mid+1,r);
    }
}
int main()
{
    scanf("%lld%lld",&n,&m);
    for(int i=1;i<=n;++i) scanf("%lld",&ini[i]);
    Buildtree(root,1,n);LL a,b,c,d;
    for(int i=1;i<=m;++i)
    {
        scanf("%lld%lld%lld",&a,&b,&c);
        if(a==1) scanf("%lld",&d),modify(root,b,c,d);
        if(a==2) printf("%lld\n",query(root,b,c));
    }
    return 0;
}

題目:P3373 【模板】線段樹 2

#include<bits/stdc++.h>
#define LL long long
#define N 100005
using namespace std;
LL n,m,p,ini[N],root,cnt;
struct sd{
    LL son[2],l,r,sum,pass,add;
    sd(){pass=1;}
}node[N*2];
inline void update(LL k){node[k].sum=node[node[k].son[0]].sum+node[node[k].son[1]].sum;}
inline void add1(LL k,LL val)
{
    node[k].pass*=val;node[k].pass%=p;
    node[k].add*=val;node[k].add%=p;
    node[k].sum*=val;node[k].sum%=p;
}
inline void add2(LL k,LL val)
{
    node[k].add+=val;node[k].add%=p;
    node[k].sum=(node[k].sum+val*(node[k].r-node[k].l+1))%p;
}

inline void pushdown(LL k)
{
    add1(node[k].son[0],node[k].pass);add1(node[k].son[1],node[k].pass);
    add2(node[k].son[0],node[k].add);add2(node[k].son[1],node[k].add);
    node[k].pass=1;node[k].add=0;
}
inline void Buildtree(LL &k,LL l,LL r)
{
    cnt++;k=cnt;node[k].l=l;node[k].r=r;
    if(l==r) node[k].sum=ini[l];
    else
    {
        LL mid=(l+r)/2;
        Buildtree(node[k].son[0],l,mid);
        Buildtree(node[k].son[1],mid+1,r);
        update(k);
    }
}
inline void modify1(LL k,LL l,LL r,LL val)
{
    if(node[k].l==l&&node[k].r==r) add1(k,val);
    else
    {
        pushdown(k);
        LL mid=(node[k].l+node[k].r)/2;
        if(r<=mid) modify1(node[k].son[0],l,r,val);
        else if(l>mid) modify1(node[k].son[1],l,r,val);
        else modify1(node[k].son[0],l,mid,val),modify1(node[k].son[1],mid+1,r,val);
        update(k);
    }
}
inline void modify2(LL k,LL l,LL r,LL val)
{
    if(node[k].l==l&&node[k].r==r) add2(k,val);
    else
    {
        pushdown(k);
        LL mid=(node[k].l+node[k].r)/2;
        if(r<=mid) modify2(node[k].son[0],l,r,val);
        else if(l>mid) modify2(node[k].son[1],l,r,val);
        else modify2(node[k].son[0],l,mid,val),modify2(node[k].son[1],mid+1,r,val);
        update(k);
    }
}
inline LL query(LL k,LL l,LL r)
{
    if(node[k].l==l&&node[k].r==r) return node[k].sum%p;
    else
    {
        pushdown(k);
        LL mid=(node[k].l+node[k].r)/2;
        if(r<=mid) return query(node[k].son[0],l,r)%p;
        else if(l>mid) return query(node[k].son[1],l,r)%p;
        else return (query(node[k].son[0],l,mid)+query(node[k].son[1],mid+1,r))%p;
    }
}
int main()
{
    scanf("%lld%lld%lld",&n,&m,&p);
    for(int i=1;i<=n;++i) scanf("%lld",&ini[i]);
    Buildtree(root,1,n); LL a,b,c,d;
    for(int i=1;i<=m;++i)
    {
        scanf("%lld",&a);
        if(a==1) scanf("%lld%lld%lld",&b,&c,&d),modify1(root,b,c,d);
        if(a==2) scanf("%lld%lld%lld",&b,&c,&d),modify2(root,b,c,d);
        if(a==3) scanf("%lld%lld",&b,&c),printf("%lld\n",query(root,b,c));
    }
    return 0;
}

By njc