线段树lazy标记2:覆盖赋值与加法修改混合

Posted wzx-rs-sthn

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了线段树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; 
}

 

以上是关于线段树lazy标记2:覆盖赋值与加法修改混合的主要内容,如果未能解决你的问题,请参考以下文章

HDU - 4578 Transformation(线段树区间修改)

线段树2

[HDOJ4578]Transformation(线段树,多延迟标记)

算法模板——线段树之Lazy标记

P2023 [AHOI2009]维护序列 - 线段树区间乘法加法

加法乘法线段树模板