zoukankan      html  css  js  c++  java
  • 线段树lazy标记2:覆盖赋值与加法修改混合

     

    题目

    Description

    给定一个正整数序列A,要求支持以下操作 
    1):  ADD a b c     表示在[a,b]上加上一个常数C。 
    2):  COVER a b c   把[a,b]整体赋值为一个常数K。 
    3):  QUERY a b     查询[a,b]的sum。 

    Input

    第一行两个正整数n、m,n表示序列长度,m表示操作数 
    第二行n个正整数,第i表示A[i]的大小 
    接下来的m行,每行有且仅有一种操作,具体和题目描述一致 
    n,m<=100000 
    其他权值都<=50000 
    小心爆int 

    Output

    对于每个询问操作,输出答案

    Sample Input

    10 10
    17 18 16 12 13 7 13 6 11 20
    QUERY 5 6
    QUERY 2 7
    ADD 1 6 13
    QUERY 4 10
    ADD 2 5 18
    COVER 2 8 11
    ADD 6 9 5
    QUERY 8 8
    ADD 6 9 18
    QUERY 5 7

    Sample Output

    20
    79
    121
    16
    79

    思路:

    很明显,这题是一道区间修改的题目;

    但是多了覆盖赋值;

    博主蒟蒻 刚写的时候是分优先级的,还打了每一个时间是覆盖赋值先,还是加法修改先;

    但是,我试了一下,wa了

    struct sbbb
    {
        ll l,r,v,f,ff,time;
    }a[1000001];
    inline ll R(ll x)
    {
        return x*2+1;
    }
    inline ll L(ll x)
    {
        return x*2;
    }
    inline void doit(ll p)
    {
        a[p].v=a[L(p)].v+a[R(p)].v;
    }
    inline void build(ll p,ll l,ll r)
    {
        a[p].l=l;a[p].r=r;
        if(l==r)
        {
            a[p].v=aa[l];
            return;
        }
        ll mid=(l+r)>>1;
        build(L(p),l,mid);
        build(R(p),mid+1,r);
        doit(p);
    }
    inline void fdo(ll p)
    {
        if(a[p].f)
        {
            ll x=a[p].f,tim=a[p].time;
            a[L(p)].v+=(a[L(p)].r-a[L(p)].l+1)*x;
            a[R(p)].v+=(a[R(p)].r-a[R(p)].l+1)*x;
            a[L(p)].f+=x;
            a[R(p)].f+=x;
            a[L(p)].time=tim;
            a[R(p)].time=tim;
            a[p].f=0;
        }
    }
    inline void ffdo(ll p)
    {
        if(a[p].ff)
        {
            ll d=a[p].ff,tim=a[p].time;
            a[L(p)].v=(a[L(p)].r-a[L(p)].l+1)*d;
            a[R(p)].v=(a[R(p)].r-a[R(p)].l+1)*d;
            a[L(p)].ff=d;
            a[R(p)].ff=d;
            a[L(p)].time=tim;
            a[R(p)].time=tim;
            a[p].ff=0;
        }
    }
    inline void push_down(ll p)
    {
        if(a[p].time==2)
        {
            ffdo(p);
            fdo(p);
        }
        else if(a[p].time==1)
        {
            fdo(p);
            ffdo(p);
        }
    }
    inline void change(ll p,ll l,ll r,ll x)
    {
        if(l<=a[p].l&&r>=a[p].r)
        {
            a[p].v+=(a[p].r-a[p].l+1)*x;
            a[p].f+=x;
            a[p].time=2;
            return;
        }
        push_down(p);
        ll mid=(a[p].l+a[p].r)>>1;
        if(l<=mid)
            change(L(p),l,r,x);
        if(r>mid)
            change(R(p),l,r,x);
        doit(p);
    }
    inline void cover(ll p,ll l,ll r,ll x)
    {
        if(l<=a[p].l&&r>=a[p].r)
        {
            a[p].v=(a[p].r-a[p].l+1)*x;
            a[p].ff=x;
            a[p].time=1;
            return;
        }
        push_down(p);
        ll mid=(a[p].l+a[p].r)>>1;
        if(l<=mid)
            cover(L(p),l,r,x);
        if(r>mid)
            cover(R(p),l,r,x);
        doit(p);
    }
    View Code

    所以,我想了一下,其实不需要考虑优先级,直接覆盖的时候把加法的lazy标记清零就好了;

    当时我想复杂了,以为会有加法的lazy标记没有清零;

    覆盖的lazy标记与加法的lazy标记是一起(一起不代表同时间)下传的;

    所以只需要在覆盖的时候把加法的lazy标记清零就好了;

    那么每一次下传的时候,是先加,还是先覆盖呢;

    我们在每一次覆盖的时候把加法的lazy标记清零了;

    所以下传时,如果加法没被清零,那么加法肯定是在覆盖之后的;

    这样就ok了;

    代码:

    #include<bits/stdc++.h>
    typedef long long ll;
    using namespace std;
    inline ll read()
    {
        ll a=0,f=1; char c=getchar();
        while (c<'0'||c>'9') {if (c=='-') f=-1; c=getchar();}
        while (c>='0'&&c<='9') {a=a*10+c-'0'; c=getchar();}
        return a*f;
    }
    ll n,m,aa[500001];
    struct sbbb
    {
        ll l,r,v,f,ff;
    }a[500001];
    inline ll R(ll x)//计算x的右节点的编号 
    {
        return x*2+1;
    }
    inline ll L(ll x)//计算x的左节点的编号
    {
        return x*2;
    }
    inline void doit(ll p)//区间维护 
    {
        a[p].v=a[L(p)].v+a[R(p)].v;
    }
    inline void build(ll p,ll l,ll r)//建树 
    {
        a[p].l=l;a[p].r=r;
        if(l==r)
        {
            a[p].v=aa[l];
            return;
        }
        ll mid=(l+r)>>1;
        build(L(p),l,mid);
        build(R(p),mid+1,r);
        doit(p);
    }
    inline void fdo(ll p)//加法lazy标记下传 
    {
        if(a[p].f)
        {
            ll x=a[p].f;
            a[L(p)].v+=(a[L(p)].r-a[L(p)].l+1)*x;
            a[R(p)].v+=(a[R(p)].r-a[R(p)].l+1)*x;
            a[L(p)].f+=x;
            a[R(p)].f+=x;
            a[p].f=0;
        }
    }
    inline void ffdo(ll p)//覆盖的lazy标记下传 
    {
        if(a[p].ff)
        {
            ll d=a[p].ff;
            a[L(p)].v=(a[L(p)].r-a[L(p)].l+1)*d;
            a[R(p)].v=(a[R(p)].r-a[R(p)].l+1)*d;
            a[L(p)].ff=d;
            a[R(p)].ff=d;
            a[L(p)].f=0;
            a[R(p)].f=0;
            //覆盖的lazy标记与加法的lazy标记是一起(一起不代表同时间)下传的;
            //所以只需要在覆盖的时候把加法的lazy标记清零就好了;
            a[p].ff=0;
        }
    }
    inline void push_down(ll p)
    {
        ffdo(p);
        fdo(p);
        //我们在每一次覆盖的时候把加法的lazy标记清零了;
        //所以下传时,如果加法没被清零,那么加法肯定是在覆盖之后的;
        //就不需要考虑优先级了 
    }
    inline void change(ll p,ll l,ll r,ll x)//区间加法修改 
    {
        if(l<=a[p].l&&r>=a[p].r)
        {
            a[p].v+=(a[p].r-a[p].l+1)*x;
            a[p].f+=x;
            return;
        }
        push_down(p);
        ll mid=(a[p].l+a[p].r)>>1;
        if(l<=mid)
            change(L(p),l,r,x);
        if(r>mid)
            change(R(p),l,r,x);
        doit(p);
    }
    inline void cover(ll p,ll l,ll r,ll x)//区间覆盖 
    {
        if(l<=a[p].l&&r>=a[p].r)
        {
            a[p].v=(a[p].r-a[p].l+1)*x;
            a[p].ff=x;
            a[p].f=0;//每一次覆盖的时候把加法的lazy标记清零;
            return;
        }
        push_down(p);
        ll mid=(a[p].l+a[p].r)>>1;
        if(l<=mid)
            cover(L(p),l,r,x);
        if(r>mid)
            cover(R(p),l,r,x);
        doit(p);
    }
    inline ll findout(ll p,ll l,ll r)//查询 
    {
        if(l<=a[p].l&&r>=a[p].r)
            return a[p].v;
        push_down(p);
        ll mid=(a[p].l+a[p].r)>>1;
        ll sum=0;
        if(l<=mid)
            sum+=findout(L(p),l,r);
        if(r>mid)
            sum+=findout(R(p),l,r);
        return sum;
    }
    int main()
    {
        n=read();m=read();
        for(ll i=1;i<=n;i++)
            aa[i]=read();
        build(1,1,n);//冬眠假期刚刚建树,我还有点糊涂 
        for(ll i=1;i<=m;i++)
        {
            char c[5];
            scanf("%s",c);
            ll x=read(),y=read();
            if(c[0]=='Q')
            {
                ll ans=findout(1,x,y);
                printf("%lld
    ",ans);
            }
            else if(c[0]=='A')
            {
                ll z=read();
                change(1,x,y,z);
            }
            else
            {
                ll z=read();
                cover(1,x,y,z);
            }
        }
        return 0;//恶意的return 0; 
    }
  • 相关阅读:
    PHP中cookie和session
    php冒泡排序 快速 选择 插入 排序
    闲置U盘变身最强大路由器
    网络工程师(CCIE)面试题大搜集
    华为与思科交换机的差别及需要注意到地方
    CISCO VSS与HSRP、VRRP、RSTP对比分析
    BNC接口、RJ45、RJ48之间区别
    CCIE找工作的七大职业走向(转载)
    2层交换机与3层交换机之间到底有什么差别!
    CISCO 6500系列交换机  简介
  • 原文地址:https://www.cnblogs.com/wzx-RS-STHN/p/13231134.html
Copyright © 2011-2022 走看看