• 线段树


    首先线段树有5个基本的操作

    分别是pushup,pushdown,modify,query,build

    1.建树

    struct node{
    	int l,r;
    	long long sum,add;
    }tr[maxn*4];
    void build(int p,int l,int r)
    {
    	tr[p].l=l;
    	tr[p].r=r;
    	if(l==r)
    	{
    		tr[p].sum=a[l];
    		return ;
    	}
    	else {
    		int mid=(tr[p].l+tr[p].r)/2;
    		build(p<<1,l,mid);
    		build(p<<1|1,mid+1,r);
    		pushup(p);
    	}
    	
    }
    

    2.首先是pushup函数

    pushup函数使用的情况是当信息被更改的时候且需要递归处理的时候使用

    void pushup(int p)
    {
    	tr[p].sum=tr[p<<1].sum+tr[p<<1|1].sum;
    }
    

    3.然后是pushdown

    试想一下,如果我们不使用懒标记的话,那么每一次区间的修改,我们就要暴力去修改,时间复杂度最坏的情况可以达到O(n),我们肯定无法承受

    于是懒标记就诞生了

    懒标记他就是在查询的时候,如果完全包含这个区间,我们是不往下查了

    反之我们在这里标记一下,add含义就是以当前节点为跟的子树中的每一个元素都加上add,注意这个add是不包

    含根自己的,当我们遇到一个区间是包含的,那么我们就直接对这个区间进行操作,这样的复杂度最坏是O(logn)的

    如果我们查询区间的话,我们就需要加上所有父节点的add值,我们在做查询的时候,我们用的每一个元素的值,都必须把它所有祖先的add值加上,这一步,我们可以在递归的过程中去实现

    void pushdown(int p) //可以类比传消息,不断向下传递
    {
    	if(tr[p].add){
    		tr[p<<1].add+=tr[p].add;
    		tr[p<<1].sum+=(tr[p<<1].r-tr[p<<1].l+1)*tr[p].add;
    		tr[p<<1|1].add+=tr[p].add;
    		tr[p<<1|1].sum+=(tr[p<<1|1].r-tr[p<<1|1].l+1)*tr[p].add;
    		tr[p].add=0;
    	}
    }
    
    

    4.modify操作

    如果我们当前修改的这个区间被包含于某个节点的管辖范围,那么我们就直接更新其维护的信息,让懒标记加上更新的值

    如果不能完全被包含,那么我们就递归左右子树,如果l<mid,那么说明左子树与查询的区间有交集,那么递归处理左子树,查询的范围保持不变

    否则我们递归右子树

    因为信息被更新了所以再最后记得pushup

    void modify(int p,int l,int r,int d)
    {
    	if(tr[p].l>=l&&tr[p].r<=r)
    	{
    		tr[p].sum+=(tr[p].r-tr[p].l+1)*d;
    		tr[p].add+=d;		
    	}
    	else {
    		pushdown(p);//不能完全包含,那么每一个部分要加的值就不一样了,我们需要先传懒标记,让其懒标记的影响消失,而后再懒标记影响后的树中进行修改操作
    		int mid=(tr[p].l+tr[p].r)/2;
    		if(l<=mid)
    		modify(p<<1,l,r,d);
    		if(r>mid)
    		modify(p<<1|1,l,r,d);
    		pushup(p);
    	}
     } 
    

    5.查询操作

    同修改的操作

    当完全包含的时候直接返回

    否则递归处理左右自身

    long long query(int p,int l,int r)
    {
    	if(tr[p].l>=l&&tr[p].r<=r)
    		return tr[p].sum;
    	else {
    		pushdown(p);
    		int mid=(tr[p].l+tr[p].r)/2;
    		long long sum=0;
    		if(l<=mid)
    			sum+=query(p<<1,l,r);
    		if(r>mid)
    			sum+=query(p<<1|1,l,r);
    		return sum;
    	}
    }
    
  • 相关阅读:
    老生长谈:css实现右侧固定宽度,左侧宽度自适应
    链接rel属性external、nofollow、external nofollow三种写法的区别
    文字无缝向上滚动
    JS中判断鼠标按键的问题
    js 字符串转换数字
    jS字符串大小写转换实现方式
    JS截取字符串
    CentOS中查看物理CPU信息的方法
    如何开启MYSQL远程连接权限
    PHP中数组排序实例学习
  • 原文地址:https://www.cnblogs.com/bangdexuanyuan/p/13726899.html
Copyright © 2020-2023  润新知