线段树Lazy-tag

由来

线段树求区间极值的时候,每次都需要修改包含在插入区间的所有节点,这样是不可能承受的!于是lazy思想也就是Lazy-tag是一个非常不错的优化。

实现

lazy思想的含义就是“要做的时候再做,不然不做”。这听起来不太可能,但实际上根据树的性质,这是可以实现的,只需要使用Lazy-tag:
对于一个节点,我们记录一个tag表示还没有传递下去的值(也就是操作时到这个节点停止了,后面没有遍历过),然后再写一个Pushdown来传递这个标记。
这里以动态统计问题为例:

void Pushdown(int id)
{
    if (tr[id].L==tr[id].R) return;
    LL tag=tr[id].tag;tr[id].tag=0;
    tr[id*2].tag+=tag;
    tr[id*2].sum+=tag*(tr[id*2].R-tr[id*2].L+1);
    tr[id*2+1].tag+=tag;
    tr[id*2+1].sum+=tag*(tr[id*2+1].R-tr[id*2+1].L+1);
}

模板(动态统计问题)

#include<cstdio>
typedef long long LL;
const int maxn=100005,maxm=100005;
int n,m;
struct zzk
{
    int L,R;
    LL now,sum; //now记录还没传下去的大小
}tr[4*maxn];
void Build(int id,int L,int R)
{
    tr[id].L=L;tr[id].R=R;
    if (L==R) return;
    int mid=L+(R-L>>1);
    Build(id*2,L,mid);Build(id*2+1,mid+1,R);
}
void Pushdown(int id)
{
    if (tr[id].L==tr[id].R) return;
    LL now=tr[id].now;tr[id].now=0;
    tr[id*2].now+=now;
    tr[id*2].sum+=now*(tr[id*2].R-tr[id*2].L+1);
    tr[id*2+1].now+=now;
    tr[id*2+1].sum+=now*(tr[id*2+1].R-tr[id*2+1].L+1);
}
void Insert(int id,int L,int R,LL x)
{
    Pushdown(id);
    if (tr[id].L==L&&tr[id].R==R)
    {
        tr[id].now=x;tr[id].sum+=x*(tr[id].R-tr[id].L+1);
        return;
    }
    int mid=tr[id].L+(tr[id].R-tr[id].L>>1);
    if (R<=mid) Insert(id*2,L,R,x); else
    if (L>mid) Insert(id*2+1,L,R,x); else
    Insert(id*2,L,mid,x),Insert(id*2+1,mid+1,R,x);
    tr[id].sum=tr[id*2].sum+tr[id*2+1].sum; //更新一下答案
}
LL Ask(int id,int L,int R)
{
    Pushdown(id);
    if (tr[id].L==tr[id].R) return tr[id].sum;
    int mid=tr[id].L+(tr[id].R-tr[id].L>>1);
    if (R<=mid) return Ask(id*2,L,R); else
    if (L>mid) return Ask(id*2+1,L,R); else
    return Ask(id*2,L,mid)+Ask(id*2+1,mid+1,R);
}
char getrch() {char ch=getchar();while (ch!='A'&&ch!='S') ch=getchar();return ch;}
int main()
{
    int i,x,y,z;
    freopen("num.in","r",stdin);
    freopen("num.out","w",stdout);
    scanf("%d%d",&n,&m);
    Build(1,1,n);
    for (i=1;i<=m;i++)
        if (getrch()=='A') scanf("%d%d%d",&x,&y,&z),Insert(1,x,y,z); else
                           scanf("%d%d",&x,&y),printf("%lld\n",Ask(1,x,y));
    return 0;
}

模板(区间极值问题)

#include<cstdio>
#include<cstdlib>
const int maxn=100005,MAXINT=((1<<30)-1)*2+1;
int n,te;
struct zzk
{
    int L,R,MAX,now; //MAX表示这个区间的最大值,now表示目前存放的还没传递下去的数
}tr[4*maxn];
int max2(int x,int y) {if (x>y) return x; else return y;}
void Build(int id,int L,int R)
{
    int mid=L+(R-L>>1);
    tr[id].L=L;tr[id].R=R;tr[id].MAX=-MAXINT;tr[id].now=-MAXINT;
    if (L<=mid&&mid+1<=R) Build(id*2,L,mid),Build(id*2+1,mid+1,R);
}
void Pushup(int id) //把值传递上来
{
    if (id==1) return;
    tr[id>>1].MAX=max2(tr[id].MAX,tr[id>>1].MAX);
}
void Pushdown(int id) //把now传递下去
{
    if (tr[id].L==tr[id].R) return;
    int now=tr[id].now;tr[id].now=-MAXINT; //now传递之后就没有了
    tr[id*2].now=max2(tr[id*2].now,now); //把now传递下去之后儿子就得到了now,可以为儿子的儿子传递
    tr[id*2].MAX=max2(tr[id*2].MAX,now); //同时修正一下MAX
    tr[id*2+1].now=max2(tr[id*2+1].now,now);
    tr[id*2+1].MAX=max2(tr[id*2+1].MAX,now);
}
void Insert(int id,int L,int R,int x)
{
    Pushdown(id); //接下来可能要用到id的儿子了,传递一下
    if (tr[id].L==L&&tr[id].R==R)
    {
        tr[id].now=x;tr[id].MAX=max2(x,tr[id].MAX); //因为下面不做了但是可能需要修正,于是记录now
        Pushup(id); //修正完毕,传递上去
        return;
    }
    int mid=tr[id].L+(tr[id].R-tr[id].L>>1);
    if (R<=mid) Insert(id*2,L,R,x); else
    if (L>mid) Insert(id*2+1,L,R,x); else
    {
        Insert(id*2,L,mid,x);
        Insert(id*2+1,mid+1,R,x);
    }
    Pushup(id); //修正完毕,传递上去
}
int Ask(int id,int L,int R)
{
    Pushdown(id); //可能要询问id的儿子,所以传递下去
    if (tr[id].L==L&&tr[id].R==R) return tr[id].MAX;
    int mid=tr[id].L+(tr[id].R-tr[id].L>>1);
    if (R<=mid) return Ask(id*2,L,R); else
    if (L>mid) return Ask(id*2+1,L,R); else
    return max2(Ask(id*2,L,mid),Ask(id*2+1,mid+1,R));
}
int getrch() {char ch=getchar();while (ch!='A'&&ch!='S') ch=getchar();return ch;}
int main()
{
    int i,x,y,z,now;
    char ch;
    freopen("max.in","r",stdin);
    freopen("max.out","w",stdout);
    scanf("%d%d",&n,&te);
    Build(1,1,n);
    while (te--)
    {
        ch=getrch();
        if (ch=='A') scanf("%d%d%d",&x,&y,&z),Insert(1,x,y,z); else
        {
            scanf("%d%d",&x,&y);now=Ask(1,x,y);
            if (now==-MAXINT) printf("The teacher is silly!\n"); else printf("%d\n",now);
        }
    }
    return 0;
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值