线段树—永久化标记优化

版权声明:本文为博主原创文章,未经博主允许不得转载,除非先点了赞。 https://blog.csdn.net/A_Bright_CH/article/details/83421105

标记永久化介绍

诞生

正常的线段树的改段求段都离不开lazy标记,lazy标记维护时要及时的更新,即人们熟知的pushdown函数。
那么能不能避免这么多次无意义的pushdown操作呢?他就是——标记永久化。

原理

举个最简单的例子,线段树区间修改,求区间最大值。
这时的lazy存的是当前这一段待下传的最大值,也就是说这个lazy最终会更新到这个线段的所有子线段。既然是他们共有的一个最大值,那拼命地往下传有什么意义呢?还不如留在这个线段上,然后后面的子线段要用的时候再从这个线段上取就好了。可以保证,每个子线段的访问前,必定会经过这个线段,所以就不用担心标记遗漏传递的问题啦。
经过这样的奇葩设计,我们的lazy就不必下传了,但并不代表他不参与求最大值。因为在操作中,对于所有子线段返回的最大值,在经过当前线段时,会被max(lazt,ret)一下,即lazy也参与了最大值的计算。
画一下重点,mx要保留,他表示的区间内的最大值;lazy要华丽变身,他表示的是当前区间以及所有子区间的最大值,所以所有有关子区间的问题他都要插一下手。mx处理的是区间完全覆盖的问题,lazy是用来插手子区间的东东。根据这些,如果能理解mx一定比lazy大(优),就完全理解了标记永久化。

实现
接下来的有关代码实现的可能要琢磨琢磨了。

更新:
一到这个区间 mx等于max(mx,val)
if 区间恰好被覆盖 then 更新lazy,结束这个区间的更新
往子区间继续更新

询问:
if 区间恰好被覆盖 then 返回mx,结束这个区间的更新
取到该区间的lazy
查询子区间的最优答案
比较出lazy和子区间返回的答案中较优的,将其返回

例题

(洛谷3437 [POI2006]TET-Tetris 3D)给定一个矩阵,初始每个位置上的元素都是0,每次选择一个子矩形,将这个子矩形内的值修改为这个子矩形内的最大值+h,求最终所有位置上的最大值。

题解

线段树套线段树+标记永久化
如果不用标记永久化的话,试想一下在外层线段树的更新是不是要精确到区间中的每一个线段树,那和暴力就没有区别了。
用永久化标记的话主要解决的是外层线段树的问题,对于完全覆盖的区间,打上永久化标记,即子节点返回的值要经永久化标记过审,所以就可以不用往后更新了。

代码

#include<cstdio>
#include<cstring>
#include<algorithm>
#define lt x<<1,xl,mid
#define rt x<<1|1,mid+1,xr
#define rootx 1,1,n
#define rooty 1,1,m
using namespace std;
const int MAXN=1100;//debug 数组开小 

template <typename Tp>
inline void read(Tp &x)
{
    x=0;char ch=getchar();bool f=false;
    while(ch<'0' || ch>'9') f|=ch=='-',ch=getchar();
    while(ch>='0' && ch<='9') x=x*10+(ch^48),ch=getchar();
}
template <typename Tp>
inline void write(Tp x)
{
    if(x>=10) write(x/10);
    write(x%10|48);
}

int n,m,Q;

struct SegY
{
    int mx[MAXN*2],tag[MAXN*2];
    void change(int x,int xl,int xr,int l,int r,int val)
    {
        mx[x]=max(mx[x],val);//max??
        if(xl==l && xr==r){tag[x]=max(tag[x],val);return ;}//debug 忘记return 
        int mid=xl+xr>>1;
        if(r<=mid) change(lt,l,r,val);
        else if(mid<l) change(rt,l,r,val);
        else change(lt,l,mid,val),change(rt,mid+1,r,val);
    }
    int query(int x,int xl,int xr,int l,int r)
    {
        if(xl==l && xr==r) return mx[x];
        int mid=xl+xr>>1,ans=tag[x];
        if(r<=mid) return max( ans , query(lt,l,r) );
        else if(mid<l) return max( ans, query(rt,l,r) );
        else return max( ans , max( query(lt,l,mid) , query(rt,mid+1,r) ));//debug 忘记return 
    }
};

struct SegX
{
    SegY mx[MAXN*2],tag[MAXN*2];
    void change(int x,int xl,int xr,int l,int r,int yl,int yr,int val)
    {
        mx[x].change(rooty,yl,yr,val);
        if(xl==l && xr==r){tag[x].change(rooty,yl,yr,val);return ;}
        int mid=xl+xr>>1;//debug mid=l+r>>1;
        if(r<=mid) change(lt,l,r,yl,yr,val);//这些地方应当是继续递归下去,而不是直接更改SegY 
        else if(mid<l) change(rt,l,r,yl,yr,val);
        else change(lt,l,mid,yl,yr,val),change(rt,mid+1,r,yl,yr,val);
    }
    int query(int x,int xl,int xr,int l,int r,int yl,int yr)
    {
        if(xl==l && xr==r) return mx[x].query(rooty,yl,yr);
        int mid=xl+xr>>1,ans=tag[x].query(rooty,yl,yr);
        if(r<=mid) return max( ans, query(lt,l,r,yl,yr) );
        else if(mid<l) return max( ans, query(rt,l,r,yl,yr) );
        else return max( ans , max( query(lt,l,mid,yl,yr) , query(rt,mid+1,r,yl,yr) ));
    }
}t;

int main()
{
    read(n),read(m),read(Q);
    while(Q--)
    {
        int d,s,h,x,y;
        read(d),read(s),read(h),read(x),read(y);x++;y++;
        int x1=x,y1=y,x2=x+d-1,y2=y+s-1;
        int mx=t.query(rootx,x1,x2,y1,y2);
        t.change(rootx,x1,x2,y1,y2,mx+h);
    }
    printf("%d\n",t.query(rootx,1,n,1,m));
    return 0;
}

猜你喜欢

转载自blog.csdn.net/A_Bright_CH/article/details/83421105
今日推荐