• 线段树区间覆盖


    线段树能支持什么操作呢

    区间覆盖,区间加,区间乘,区间查询,单点修改,可能远远不止这些,但本人能力有限,欢迎大家指出错误,一起学习呀

    对区间进行操作,我们肯定要使用懒标记

    那么懒标记的下放顺序就是一个问题

    我们目前需要处理3个懒标记分别记为(lazy,add,mul;)

    优先级应该是(lazy>mul>add)

    为什么呢,首先是因为如果区间覆盖,那么前面更新的区间加,区间乘都会哑然失色,所以说区间覆盖才是巨佬,那么如果有区间覆盖的

    懒标记的话,我们当然是要先下放它,并把区间加,区间乘的懒标记设为没有

    按照顺序的,仿照上面的依次考虑乘法标记,和加法标记

    还有一种思想是需要我们知道,就是我们的懒标记是用到,我们就(pushdown),用不到,就让它挂着就好,如果每次修改,我们都把懒标记

    全部下放一边,那和暴力没有什么差别了,懒标记也就是失去了它的意义,总归,它的作用就是,当前修改的区间用的着的,我就下放,

    用不着的,就在这个节点,随着区间的更新懒标记就行

    结构体
    struct node{
        int l,r;//控制边界
        int lazy,mul,add,sum;//按照优先级的顺序进行定义
    }tr[N*4];
    

    懒标记的下放,重中之重,打起精神了

    void pushdown(int p)//因为懒标记的下放是对当前节点的左右儿子产生影响的,所以只用修改左右儿子的信息,而当前点的信息除了lazy外,都不用进行修改
    {
    	if(tr[p].lazy!=-1)
    	{
    		tr[p<<1].sum=(tr[p<<1].r-tr[p<<1].l+1)*tr[p].lazy;
    		tr[p<<1|1].sum=(tr[p<<1|1].r-tr[p<<1|1].l+1)*tr[p].lazy;
    		tr[p<<1].lazy=tr[p<<1|1].lazy=tr[p].lazy;
    		tr[p].lazy=-1;
    	}
    	tr[p<<1].mul*=tr[p].mul;//乘法标记
    	tr[p<<1|1].mul*=tr[p].mul;
    	tr[p<<1].sum*=tr[p].mul;//区间和直接乘上乘法标记
    	tr[p<<1|1].sum*=tr[p].mul;
    	tr[p<<1].add=tr[p<<1].add*tr[p].mul+tr[p].add;//加法标记
    	tr[p<<1|1].add=tr[p<<1|1].add*tr[p].mul+tr[p].add;
    	tr[p<<1].sum+=(tr[p<<1].r-tr[p<<1].l+1)*tr[p].add;
    	tr[p<<1|1].sum+=(tr[p<<1|1].r-tr[p<<1|1].l+1)*tr[p].add;
    	tr[p].mul=1;//乘法标记最开始也应该是为1的,表示没有
    	tr[p].add=0;
    }
    

    剩下的需要进行修改的部分就是在修改(update)函数里面了

    void modify(int p,int l,int r,int add,int mul)
    {
    	if(tr[p].l>=l&&tr[p].r<=r)
    	{
    		if(tr[p].lazy!=-1)
    		{
    			tr[p<<1].sum=(tr[p<<1].r-tr[p<<1].l+1)*tr[p].lazy;
    			tr[p<<1|1].sum=(tr[p<<1|1].r-tr[p<<1|1].l+1)*tr[p].lazy;
    			tr[p<<1].lazy=tr[p<<1|1].lazy=tr[p].lazy;
    			tr[p].lazy=-1;
    			return;
    		}
    		else
    		{
    			tr[p].sum=((long long)tr[p].sum*mul+(tr[p].r-tr[p].l+1)*add)%mod;
    			tr[p].add=((long long)tr[p].add*mul+add)%mod;
    			tr[p].mul=((long long)tr[p].mul*mul)%mod;
    			return ;
    		}
    	}
    	pushdown(p);
    	int mid=(tr[p].l+tr[p].r)/2;
    	if(l<=mid)modify(p<<1,l,r,add,mul);
    	if(r>mid) modify(p<<1|1,l,r,add,mul);
    	pushup(p);
    }
    

    注意:由于我们的修改函数递归进行处理的,所以程序在到达递归边界的时候,一定记得return,否则会进

    入死循环

    这里有一道例题,我们来看一下(CF343D Water Tree)

    这道题就是让我们写出一个数据结构,使其能够满足下列操作

    1.区间赋值
    
    2.子树赋值
    
    3.单点查询(区间会查询,单点不会查询,不丢人吗)
    

    子树——树剖,树剖是肯定没有跑的了,前面的还是极其套路的(dfs1,dfs2,query,build)

    关键就在于修改的操作,我们需要修改的部分也就是区间覆盖的懒标记出现的位置

    一个在(pushdown)里面

    inline void pushdown(int p)
    {
    	if(tr[p].lazy!=-1)
    	{
    		tr[p<<1].sum=(tr[p<<1].r-tr[p<<1].l+1)*tr[p].lazy;
    		tr[p<<1].lazy=tr[p].lazy;
    		tr[p<<1|1].sum=(tr[p<<1|1].r-tr[p<<1|1].l+1)*tr[p].lazy;
    		tr[p<<1|1].lazy=tr[p].lazy;
    		tr[p].lazy=-1;
    	}
    }
    

    一个在(update)函数里面

    inline void update(int p,int l,int r,int k)
    {
    	if(tr[p].l>=l&&tr[p].r<=r)
    	{
    		tr[p].sum=(tr[p].r-tr[p].l+1)*k;//这个区间里面的所有数都是k了,那么区间和就是区间长度*k
    		tr[p].lazy=k;//标记一下,在以后更新子节点信息的时候用的着
    		return ;
    	}
    	pushdown(p);
    	int mid=(tr[p].l+tr[p].r)/2;
    	if(l<=mid)	update(p<<1,l,r,k);
    	if(r>mid)	update(p<<1|1,l,r,k);
    	pushup(p);
    }
    

    完结撒花

  • 相关阅读:
    IIS发布问题解决
    创建Core项目使用IdentityServer4
    通过数据库名称字符串 反射获取数据并刷新对应缓存
    MVC模式下unity配置,报错“No connection string named '**Context' could be found in the application config file”
    Docker巨轮的航行之路-基础知识篇
    C#之LINQ
    Js调试中不得不知的Console
    jQuery中是事件绑定方式--on、bind、live、delegate
    前端常用技术概述--Less、typescript与webpack
    ES6学习之变量的解构赋值
  • 原文地址:https://www.cnblogs.com/bangdexuanyuan/p/14002469.html
Copyright © 2020-2023  润新知