线段树的简单实现(引入lazy_tag)

线段树是一种基于分治思想的二叉树,每一个结点都对应一个区间,叶子节点的区间L=R,非叶子结点,左孩子区间为**[L,(L+R)/2],右孩子区间为[(L+R)/2+1,R].所以和树状数组相比,线段树能更好的维护一个区间= =这也是其优势所在
那么对于每一个结点,我们可以维护什么信息呢?
区间的最值,区间和等等,具体根据题目的要求来确定;
以下代码和示例均维护区间最值(最大值);
以下是一些常用的操作,代码块里均有实现方式
图源@bilibili 算法训练营
线段树的简单实现(引入lazy_tag)_第1张图片
线段树的简单实现(引入lazy_tag)_第2张图片
线段树的简单实现(引入lazy_tag)_第3张图片
看到这可能就有疑问了,线段树的最大作用不是维护一个区间吗,这里除了区间查询并没有区间更新的操作。那么假设我们要对某个区间[L,R]上的所有元素都加减某个元素v,如果按照点更新的方式,毫无疑问时间复杂度为nlogn,这个时间复杂度是很不理想的,于是下面引入
懒标记(Lazy_tag)**;
对于某个区间操作,因为后面的操作不一定会涉及这个区间的子区间,所以我们不妨懒一点,既然没有查询,那么这次区间操作我并没有必要更新所有的子区间,我只更新最大的那个,并且标记一下,代表这个区间是进行过操作的就行,等到下次操作要用到这个区间我们再对子区间进行更新。这样的话,区间更新的时间复杂度可以近似为O(logn),这是很理想的;
大家可以自行查阅文章

#include
using namespace std;
const int N=10005;
const int INF=0x3f3f3f3f;
int n,a[N];
struct node
{
	int l,r,mx;//左右端点以及最值   这里也可以换成区间值,根据需求就行
	int lazy_tag;//懒惰标记; 
}tree[N*4];//一定要开4倍n空间;
void f(int k,int lazy_tag)
{
	tree[k].lazy_tag+=lazy_tag;
	tree[k].mx+=lazy_tag;
}
void pushdown(int k)//懒惰标记下传
{
	f(k*2,tree[k].lazy_tag);
	f(k*2+1,tree[k].lazy_tag);
	tree[k].lazy_tag=0;//懒惰标记置零 
} 
void build(int k,int l,int r)//递归建立线段树,k表示存储下标;
{
	tree[k].l=l;
	tree[k].r=r;
	if(l==r)//递归出口 
	{
		tree[k].mx=a[l];//最值就是a[l]存储的值 
		return;
	}
	int mid=l+r>>1;
	build(k*2,l,mid);
	build(k*2+1,mid+1,r);
	tree[k].mx=max(tree[k*2].mx,tree[k*2+1].mx);//当前区间最值即为左右孩子的最值; 
} 
void push(int k,int l,int r,int v)//区间更新,区间加减v
{
	if(tree[k].l>=l&&tree[k].r<=r)//如果区间被覆盖 
	{
		tree[k].mx+=v;
		tree[k].lazy_tag=v;//打上懒惰标记 
		return;
	}
	pushdown(k);
	int mid=(tree[k].l+tree[k].r)>>1;
	if(l<=mid)
	push(k*2,l,r,v);
	if(r>mid)
	push(k*2+1,l,r,v);
	tree[k].mx=max(tree[k*2].mx,tree[k*2+1].mx);
} 
void update(int k,int i,int v)//点更新,将a[i]修改更新为v,并维护线段树
{
	if(tree[k].l==tree[k].r&&tree[k].l==i)//如果是叶子结点而且l=i; 
	{
		tree[k].mx=v;
		return;
	}
	int mid=(tree[k].l+tree[k].r)/2; 
	if(i<=mid)//在左子树
	update(k*2,i,v);
	else
	update(k*2+1,i,v);
	tree[k].mx=max(tree[k*2].mx,tree[k*2+1].mx);//回归时更新最值维护线段树; 
} 
int query1(int k,int l,int r)//用区间覆盖的方法 查询区间l-r的最值 (l,r)不改变; 
{
	 if(tree[k].l>=l&&tree[k].r<=r)//区间覆盖
     return tree[k].mx;
     if(tree[k].lazy_tag)//先下传懒惰标记 
     pushdown(k);
     int mid=(tree[k].l+tree[k].r)/2;
     int Max=-INF;//负无穷 
     if(l<=mid)
     Max=max(Max,query1(k*2,l,r));
     if(r>mid)
     Max=max(Max,query1(k*2+1,l,r));
     return Max;
} 
int query2(int k,int l,int r)//区间相等查询l-r的最值  (l,r)改变
{
	if(tree[k].l==l&&tree[k].r==r)
	return tree[k].mx;
	if(tree[k].lazy_tag)//先下传懒惰标记 
	pushdown(k);
	int mid=(tree[k].l+tree[k].r)/2;
	if(r<=mid)//左子树查询 
	return query2(k*2,l,r);
	else if(l>mid)
	return query2(k*2+1,l,r);
	else
	return max(query2(k*2,l,mid),query2(k*2+1,mid+1,r));//左右子树分别查询缩小范围 
} 
void print(int k)
{
	if(tree[k].mx)
	{
		cout<<k<<" "<<tree[k].l<<" "<<tree[k].r<<" "<<tree[k].mx<<endl;
		print(k*2);
		print(k*2+1);
	}
} 
int main()
{
	cin>>n;
	for(int i=1;i<=n;i++)
	cin>>a[i];
	build(1,1,n);
	int l,r,v,i;
	cin>>l>>r;
	cout<<query1(1,l,r)<<endl;
	cout<<query2(1,l,r)<<endl;
	cin>>i>>v;
	update(1,i,v);
	cout<<query1(1,l,r)<<endl;
	cout<<query2(1,l,r)<<endl;
	push(1,l,r,v);
	cout<<query1(1,l,r)<<endl;
	cout<<query2(1,l,r)<<endl;
	cin>>l>>r;
	cout<<query1(1,l,r)<<endl;
	cout<<query2(1,l,r)<<endl;
	return 0;
}

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