线段树应用——动态开点

动态开点的引入

 在一些计数问题中,线段树用于维护值域(一段取值范围),这样的线段树也称为权值线段树。有些时候,为了降低空间复杂度,我们可以不建出整颗线段树的结构,而是在最初只建立一个根节点,代表整个区间,当我们需要访问线段树的某棵子树时(某个区间时),再建立代表这个子区间的节点。采用这种方法维护的线段树就称为动态开点的线段树。

动态开点线段树~!

 动态开点线段树抛弃了完全二叉树父子节点的2倍编号规则,改为使用变量记录左右子儿子节点的编号(相当于指针)。同时,它也不再保存每个节点代表的区间,而是在每次递归访问的过程中作为参数传递。下面是一个动态开点的线段树的节点编号。

struct SegmentTree{
	int lc,rc;//左右子节点的编号 
	int dat;//区间最大值 
}t[MAXM];

int root=0,tot=0;

inline int build(){//动态开点 
	tot++;
	t[tot].l=t[tot].r=t[tot].dat=0;
	return tot;
}

tot=0;
root=build();

 下面的代码对线段树单点修改的过程稍加修改,实现了在动态开点线段树中把 x x x位置加上 d e l t a delta delta,同时维护区间最大值的过程。

void pushup(int p){t[p].dat=max(t[t[p].lc].dat,t[t[p].rc].dat);}

void update(int p,int l,int r,int x,int delta){
	if(l==r){
		t[p].dat+=delta;
		return;
	}
	int mid=(l+r)/2;
	if(x<=mid){
		if(!t[p].lc) t[p].lc=build();
		update(t[p].lc,l,mid,x,delta); 
	}
	else{
		if(!t[p].rc) t[p].rc=build();
		update(t[p].rc,mid+1,r,x,delta);
	}
	pushup(p);
}

update(root,1,n,x,delta);

 常规线段树的其它操作也可以通过类似的变动,在动态开点的线段树上实现。一棵维护值域 [ 1 , n ] [1,n] [1,n]的动态开点线段树在经历 m m m次单点操作后,节点数量的规模为 O ( m log ⁡ n ) O(m\log n) O(mlogn)。最终至多有 2 n − 1 2n-1 2n1个节点。

你可能感兴趣的:(数据结构,c++,数据结构)