zoukankan      html  css  js  c++  java
  • 线段树2对于Pushdown的理解

    关于“线段树2”~3373

    最近才把这玩意儿搞出来,代码和解说如下:

        #include<cstdio>
        #include<cstring>
        using namespace std;
        const int N=100005;
        struct sd{
            int son[2],l,r;
            long long sum,add,pass;
            sd()
            {
                memset(this,0,sizeof(this));
                pass=1;
            }
        }node[N*2];
        int ini[N],root,cnt=0,q;
        void Buildtree(int &k,int l,int r)
        {
            k=++cnt;
            node[k].l=l;
            node[k].r=r;
            if(l==r)
            {
                node[k].sum=ini[l];
            }
            else
            {
                int mid=(l+r)/2;
                Buildtree(node[k].son[0],l,mid);
                Buildtree(node[k].son[1],mid+1,r);
                node[k].sum=node[node[k].son[0]].sum+node[node[k].son[1]].sum;
            }
        }
        void pushdown(int k)
        {
            sd L,R,Main;
            L=node[node[k].son[0]];
            R=node[node[k].son[1]];
            Main=node[k];
            L.sum=(L.sum*Main.pass+Main.add*(L.r-L.l+1))%q;
            R.sum=(R.sum*Main.pass+Main.add*(R.r-R.l+1))%q;
            L.pass=(L.pass*Main.pass)%q;
            R.pass=(R.pass*Main.pass)%q;
            L.add=(L.add*Main.pass+Main.add)%q;
            R.add=(R.add*Main.pass+Main.add)%q;
            Main.add=0; Main.pass=1;
            node[k]=Main;//☣
            node[node[k].son[0]]=L;
            node[node[k].son[1]]=R;
            return;
        }
        void modify1(int k,int ql,int qr,int val)
        {
            if(node[k].l==ql&&node[k].r==qr)
            {
                node[k].add=(node[k].add+val)%q;
                node[k].sum=(node[k].sum+val*(node[k].r-node[k].l+1))%q;
            }
            else
            {
                pushdown(k);
                int mid=(node[k].l+node[k].r)/2;
                if(ql>mid) modify1(node[k].son[1],ql,qr,val);
                else if(qr<=mid) modify1(node[k].son[0],ql,qr,val);
                else 
                {
                    modify1(node[k].son[0],ql,mid,val);
                    modify1(node[k].son[1],mid+1,qr,val);
                }
                node[k].sum=node[node[k].son[0]].sum+node[node[k].son[1]].sum;
            }
        }
        void modify2(int k,int ql,int qr,int val)
        {
            if(node[k].l==ql&&node[k].r==qr)
            {
                //node[k].add=(node[k].add*val)%q;//一个存疑点
                node[k].pass=(node[k].pass*val)%q;
                node[k].sum=(node[k].sum*val)%q;
            }
            else
            {
                pushdown(k);
                int mid=(node[k].l+node[k].r)/2;
                if(ql>mid) modify2(node[k].son[1],ql,qr,val);
                else if(qr<=mid) modify2(node[k].son[0],ql,qr,val);
                else 
                {
                    modify2(node[k].son[0],ql,mid,val);
                    modify2(node[k].son[1],mid+1,qr,val);
                }
                node[k].sum=node[node[k].son[0]].sum+node[node[k].son[1]].sum;
            }
        }
        long long query(int k,int l,int r,int n)
        {
            if(node[k].l==l&&node[k].r==r)
            {
                pushdown(k);
                return node[k].sum;
            }
            else
            {
                pushdown(k);
                int mid=(node[k].l+node[k].r)/2;
                if(mid>=r) return query(node[k].son[0],l,r,n);
                else if(mid<l) return query(node[k].son[1],l,r,n);
                else 
                {
                    return query(node[k].son[0],l,mid,n)+query(node[k].son[1],mid+1,r,n);
                }
            }
        }
        int main()
        {
            int m,n;
            scanf("%d%d%d",&m,&n,&q);
            int a,b,c,d;
            for(int i=1;i<=m;++i)
            {
                scanf("%d",&ini[i]);
            }
            Buildtree(root,1,m+5);
            for(int i=1;i<=n;++i)
            {
                scanf("%d",&d);
                if(d==1)
                {
                    scanf("%d%d%d",&a,&b,&c);
                    modify2(root,a,b,c);
                }
                else if(d==2)
                {
                    scanf("%d%d%d",&a,&b,&c);
                    modify1(root,a,b,c);
                }
                else
                {
                    scanf("%d%d",&a,&b);
                    printf("%lld
    ",query(root,a,b,q)%q);
                }
            }
            return 0;
        }
    

    其中Pushdown是本题的精髓


    正常的两种Pushdown写法:

    方法一:

        void pushdown(int k)
        {
            sd L,R,Main;
            L=node[node[k].son[0]];
            R=node[node[k].son[1]];
            Main=node[k];
            L.sum=(L.sum*Main.pass+Main.add*(L.r-L.l+1))%q;
            R.sum=(R.sum*Main.pass+Main.add*(R.r-R.l+1))%q;
            L.pass=(L.pass*Main.pass)%q;
            R.pass=(R.pass*Main.pass)%q;
            L.add=(L.add*Main.pass+Main.add)%q;
            R.add=(R.add*Main.pass+Main.add)%q;
            Main.add=0; Main.pass=1;
            node[k]=Main;
            node[node[k].son[0]]=L;
            node[node[k].son[1]]=R;
            return;
        }
    

    方法二:

        void Pushdown(int k)
        {
            node[node[k].son[0]].sum=(node[node[k].son[0]].sum*node[k].pass + node[k].add* ( node[node[k].son[0]].r - node[node[k].son[0]].l+1) ) %q;
               node[node[k].son[1]].sum=(node[node[k].son[1]].sum*node[k].pass + node[k].add*( node[node[k].son[1]].r - node[node[k].son[1]].l+1) ) %q;
               node[node[k].son[0]].pass=(node[node[k].son[0]].pass*node[k].pass)%q;
               node[node[k].son[1]].pass=(node[node[k].son[1]].pass*node[k].pass)%q;
            node[node[k].son[0]].add=(node[node[k].son[0]].add*node[k].pass+node[k].add)%q;
               node[node[k].son[1]].add=(node[node[k].son[1]].add*node[k].pass+node[k].add)%q;
               node[k].add=0; node[k].pass=1;
               return 0;
        }
    

    WA掉的方法:

        void Pushdown(int k)
        {
            node[node[k].son[0]].add+=node[k].add;//写错了
            node[node[k].son[1]].add+=node[k].add;//写错了
            node[node[k].son[0]].pass*=node[k].pass;
            node[node[k].son[1]].pass*=node[k].pass;
            node[node[k].son[0]].sum=node[node[k].son[0]].sum*node[node[k].son[0]].pass+(node[node[k].son[0]].r-node[node[k].son[0]].l+1);
            node[node[k].son[1]].sum=node[node[k].son[1]].sum*node[node[k].son[1]].pass+(node[node[k].son[1]].r-node[node[k].son[1]].l+1);
            node[k].add=0;  node[k].pass=1;
            return 0;
        }
    

    如何理解此Pushdown呢(大家可以对照那份WA掉的看)?


    首先前两行是来更新父节点下左右儿子的和一定要遵循*先乘后加*的原则,并且在加的时候,一定不忘乘上区间的长度这样更新才不会错!!后面四步是基于要“薪火相传”的原则,以后Pushdown的时候可以Pushdown给他们自己的儿子。

    所以中间两步是先来传乘法(pass)直接用儿子的乘(L&R.pass)乘上父亲转下来的乘(Main.pass)。

    后面两步则是传下父亲的加,但是要注意,传加的时候应该先用儿子原有的加乘上父亲的乘(因为我们遵循“*先乘后加*”的原则,在第一阶段,原有的加应该会被父亲的乘成倍扩大(和区间和(sum)一起)!!!)

    关于modify1,modify2中的改变

    本人写程序的时候,可能脑袋有点抽,modify1和操作1不是对应的,modify1对应操作2~~~~~~

    先来看一下和乘有关的modify
        void modify2(int k,int ql,int qr,int val)
        {
            if(node[k].l==ql&&node[k].r==qr)
            {
                node[k].add=(node[k].add*val)%q;
                node[k].pass=(node[k].pass*val)%q;
                node[k].sum=(node[k].sum*val)%q;
            }
            //后面还有省略,这里只是相对于线段树1有稍微改变的地方
    

    乘法modify主要要注意的问题,还是要注意要更新加(add)中的东西(我想了一下,介于有Pushdown我也觉得没有那个必要)然后就是正常更新儿子中乘(pass)和总和(sum)。

    再来看一下和加有关的modify
        }    void modify1(int k,int ql,int qr,int val)
        {
            if(node[k].l==ql&&node[k].r==qr)
            {
                node[k].add=(node[k].add+val)%q;
                node[k].sum=(node[k].sum+val*(node[k].r-node[k].l+1))%q;
            }
            //后面还有省略,这里只是相对于线段树1有稍微改变的地方
    

    正常修改儿子中的加(add),但是最后一定不要忘记修改儿子总和时(sum)(因为有Pushdown,所以我们不用考虑“先乘后加”原则),只要记住乘上区间长度即可。

    最后(几点提醒)

    1、Pushdown中一定不要忘了把建立的临时变量里的东西传回node中!!!血的教训啊!耗了我一个多小时!!!*

    2、modify中Pushdown已经帮你做了很多事情了,所以不用think to much!

    3、更多警告可以看一下我的线段树Coding Caution

    P.S:有人可能会问我,为什么在Pushdown中更新下面的总值的时候为什么不把要更新的点的Pass也乘进去,是因为在modify的时候我已经用他原来的Pass值更新过一遍了,当然不需要乘进去,那又会有人问,为什么要把pass的值和add的值又更新到要更新的点的Pass和add上呢?这个则是因为后面又要Pushdown的时候,一定要连着这个一起Pushdown下去。

    2018.10.16

    下面放一个写的比较清晰的线段树2:

    AC code:

    #include<bits/stdc++.h>
    #define N 100004
    #define LL long long
    using namespace std;
    struct sd{
        LL son[2],l,r,pass,add,sum;
        sd(){pass=1;}
    }node[N*10];
    LL p,ini[N],root,cnt,n,m;
    void update(LL k){node[k].sum=node[node[k].son[0]].sum+node[node[k].son[1]].sum;}
    void add_pass(LL k,LL val)
    {
        node[k].pass=(node[k].pass*val)%p;node[k].sum=(node[k].sum*val)%p;
        node[k].add=(node[k].add*val)%p;
    }
    void add_add(LL k,LL val)
    {
        node[k].sum=(val*(node[k].r-node[k].l+1)+node[k].sum)%p;
        node[k].add=(node[k].add+val)%p;
    }
    void pushdown(LL k)
    {
        add_pass(node[k].son[0],node[k].pass); add_pass(node[k].son[1],node[k].pass);node[k].pass=1;
        add_add(node[k].son[0],node[k].add); add_add(node[k].son[1],node[k].add);node[k].add=0;
    }
    void Buildtree(LL &k,LL l,LL r)
    {
        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_pass(LL k,LL l,LL r,LL val)
    {
        if(node[k].l==l&&node[k].r==r) add_pass(k,val);
        else
        {
            pushdown(k); LL mid=(node[k].l+node[k].r)/2;
            if(r<=mid) modify_pass(node[k].son[0],l,r,val);
            else if(l>mid) modify_pass(node[k].son[1],l,r,val);
            else modify_pass(node[k].son[0],l,mid,val),modify_pass(node[k].son[1],mid+1,r,val);
            update(k);
        }
    }
    void modify_add(LL k,LL l,LL r,LL val)
    {
        if(node[k].l==l&&node[k].r==r) add_add(k,val);
        else
        {
            pushdown(k); LL mid=(node[k].l+node[k].r)/2;
            if(r<=mid) modify_add(node[k].son[0],l,r,val);
            else if(l>mid) modify_add(node[k].son[1],l,r,val);
            else modify_add(node[k].son[0],l,mid,val),modify_add(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%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("%d%d%d",&n,&m,&p);
        for(int i=1;i<=n;++i) scanf("%d",&ini[i]);
        Buildtree(root,1,n);int a,b,c,d;
        for(int i=1;i<=m;++i)
        {
            scanf("%d",&a);
            if(a==1) scanf("%d%d%d",&b,&c,&d),modify_pass(root,b,c,d);
            if(a==2) scanf("%d%d%d",&b,&c,&d),modify_add(root,b,c,d);
            if(a==3) scanf("%d%d",&b,&c),printf("%d
    ",query(root,b,c));
        }
        return 0;
    }
    

    指针版的线段树戳这里☞指针版的线段树

    谢谢采纳!
  • 相关阅读:
    【Vertica】vertica配置ODBC
    【oracle】【demo】oracle批量造数据
    【oracle】oracle系统视图nls_database_parameters
    【oracle】interval函数
    【DB2】DB2帮助文档
    【oracle】【sql】oracle去重
    【oracle】【sql】oracle分组
    【oracle】【性能】case表达式
    oracle EXP-00028
    Nginx系列~Nginx服务启动不了
  • 原文地址:https://www.cnblogs.com/mudrobot/p/13330784.html
Copyright © 2011-2022 走看看