论打含有Pushdown线段树的正确姿势

Posted mudrobot

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了论打含有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
",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
",query(root,b,c));
    }
    return 0;
}

By njc

以上是关于论打含有Pushdown线段树的正确姿势的主要内容,如果未能解决你的问题,请参考以下文章

poj 3468 整理一下线段树的写法

[知识点]线段树标记永久化

JZYZOJ1527 [haoi2012]高速公路 线段树 期望

[线段树系列] LCT打延迟标记的正确姿势

[UOJ218]火车管理

关于线段树的一些学习笔记——(无限施工中)