线段树—模板

线段树常见操作

  • build 建树
  • update 更新
  • query 查询
  • pushup 向上回溯
  • pushdown 向下延迟更新(延迟标记)

建线段树:

//预编译命令,做符号代换
#define lson (gjd<<1)
#define rson (gjd<<1|1)

//gjd表示当前结点,[l,r]表示区间范围
void build(int gjd,int l,int r){
    tree[gjd].l=l;
    tree[gjd].r=r;
    if(l==r){
        tree[gjd].max=tree[gjd].sum=a[l];
        return;
    }
    int mid=(l+r)>>1;
    build(lson,l,mid);
    build(rson,mid+1,r);
    tree[gjd].sum=tree[lson].sum+tree[rson].sum;
    tree[gjd].max=max(tree[lson].max,tree[rson].max);
}

调用时:build(1,1,n);

更新—单点更新

void update(int gjd,int now,int ans){
	if(tree[gjd].l==tree[gjd].r){//找到对应的叶子结点
		tree[gjd].sum+=ans;//tree[gjd].max=ans;
		return;
	}
	int mid=(tree[gjd].l+tree[gjd].r)>>1;
	if(now<=mid) update(lson,now,ans);
	else update(rson,now,ans);
    //回溯更新父结点信息
	tree[gjd].sum=tree[lson].sum+tree[rson].sum;
	tree[gjd].max=max(tree[gjd].max,tree[rson].max);
}

查询—查询区间和

//当前结点为gjd,要查询的区间为[l,r] 
int query(int gjd,int l,int r){
	//如果结点表示的区间是查询区间的真子集 
	if(l<=tree[gjd].l&&tree[gjd].r<=r)
	   return tree[gjd].sum;
	int mid=(tree[gjd].l+tree[gjd].r)>>1;
	if(r<=mid) return query(lson,l,r);
	else if(l>mid) return query(rson,l,r);
	else return (query(lson,l,r)+query(rson,l,r)); 
}

向上回溯

该函数就是由子结点递归回来,修改父结点中的信息。由于一般建树和更新都有这个相同的操作,因此我们可以写成一个函数,简化代码量

void pushup(int gjd){
    tree[gjd].sum=tree[lson].sum+tree[rson].sum;
    tree[gjd].max=max(tree[lson].max,tree[rson].max);
}

延迟标记

做区间更新时,如果要更新的区间能够完全覆盖当前节点表示的区间,则在此节点上做个标记(表示此节点曾被修改,但子节点尚未被更新),不再继续向下更新,同时在回溯时更新父节点的信息。
 如果在之后的维护或查询过程中需要对这个节点的某个儿子递归地进行处理,则将这个标记分解,传递给它的两个儿子节点。
 这种在需要的时候才进行分解的做法,使我们整体的时间复杂度仍在O(log2N) 的水平上。

//把当前结点gjd的延迟标记下放到左右儿子
void pushdown(int gjd){
	if(tree[gjd].lazy){//此结点有延迟标记 
		int lz=tree[gjd].lazy;
		tree[gjd].lazy=0;//记得清零 
		tree[lson].lazy+=lz;
		tree[rson].lazy+=lz;
		tree[lson].sum+=lz*(tree[lson].r-tree[lson].l+1);
		tree[rson].sum+=lz*(tree[rson].r-tree[rson].l+1);
	}
} 

区间更新

void update(int gjd,int l,int r,int ans){
	//更新区间完全覆盖结点表示的区间
	if(l<=tree[gjd].l&&tree[gjd].r<=r){
		tree[gjd].lazy+=ans;
		tree[gjd].sum+=ans*(tree[gjd].r-tree[gjd].l+1);
		return;
	}
	//如果不能完全覆盖,此时需要向下递归,要下放标记 
	pushdown(gjd);
	int mid=(tree[gjd].l+tree[gjd].r)>>1;
	if(l<=mid) update(lson,l,r,ans);
	if(r>mid) update(rson,l,r,ans);
	pushup(gjd);//更新完回来记得要更新当前结点的信息 
}

区间查询(求和)

int query(int gjd,int l,int r){
	//更新区间完全覆盖结点表示的区间 
	if(l<=tree[gjd].l&&tree[gjd].r<=r)
	   return tree[gjd].sum;
	//如果不能完全覆盖,此时需要向下递归,要下放标记
	pushdown(gjd);
	int mid=(tree[gjd].l+tree[gjd].r)>>1;
	int ans=0;
	if(l<=mid) ans+=query(lson,l,r);
	if(r>mid) ans+=query(rson,l,r);
	return ans; 
}

如有错误,欢迎大佬们在评论区指正~

#一名爱打篮球的oier#

posted @ 2024-02-20 17:50  __kw  阅读(56)  评论(0)    收藏  举报