关于线段树

线段树是一种数据结构,用于处理区间动态查询和更新问题,每个操作的时间复杂度基本为O(logn)。它包括单点更新、区间查询等功能,通过二叉树结构实现,支持快速查找区间最小值或最大值,并能进行区间值的修改。
摘要由CSDN通过智能技术生成

线段树

线段树,类似区间树,它在各个节点保存一条线段(数组中的一段子数组),主要用于高效解决连续区间的动态查询问题,由于二叉结构的特性,它基本能保持每个操作的复杂度为O(logn)。

功能

单点更新 

建树 

如果父节点的下标为id,那么左儿子下标为2*id,右儿子下标为2*id+1。

void build(int id,int l,int r)
{
	if(l==r) //左端点与右端点相等
	{
		tr[id]=a[l];
		return ;
	} 
	int mid=(l+r)/2;
	build(id*2,l,mid); //左子树 
	build(id*2+1,mid+1,r);//右子树
	//更新父节点 
	tr[id]=min(tr[id*2],tr[id*2+1]);
	
}

值的更新

//单点更新
//id 树结点编号,l,r为其对应的区间,将下标为x的元素修改为v 
void genxin(int id,int l,int r,int x,int v)
{
	//区间长度为1,也就是左端点等于右端点  找到了 
	if(l==r)
	{
		tr[id]=v;
		return ; 
	}
	//否则 找到x所在的区间
	int mid=(l+r)/2;
	//在左区间 
	if(x<=mid) genxin(id*2,l,mid,x,v);
	//右区间 
	else genxin(id*2+1,mid+1,r,x,v);
	
	tr[id]=min(tr[id*2],tr[id*2+1]); //查询最小值
	 
} 

区间查询

int find(int id,int l,int r,int x,int y) 
{
	if(x>r||y<l) return 0;
	//该区间被包含在要查询的区间内 
	if(x<=l&&r<=y) return tr[id]; 
	int mid=(l+r)/2;
	int ans=INT_MAX;
	//与左半区间有交集 
	if(x<=mid)  ans=min(ans,find(id*2,l,mid,x,y)); //看求的是区间内的最小值还是最大值 
	//与右半区间有交集 
	else ans=min(ans,find(id*2+1,mid+1,r,x,y));
    return ans;  
}
完整代码
#include<bits/stdc++.h>
using namespace std;
const int N=1e6+9;
int tr[N];
int a[N];

//建树 
void build(int id,int l,int r)
{
	if(l==r) //左端点与右端点相等
	{
		tr[id]=a[l];
		return ;
	} 
	int mid=(l+r)/2;
	build(id*2,l,mid); //左子树 
	build(id*2+1,mid+1,r);//右子树
	//更新父节点 
	tr[id]=min(tr[id*2],tr[id*2+1]);
	
}
//单点更新
//id 树结点编号,l,r为其对应的区间,将下标为x的元素修改为v 
void genxin(int id,int l,int r,int x,int v)
{
	//区间长度为1,也就是左端点等于右端点  找到了 
	if(l==r)
	{
		tr[id]=v;
		return ; 
	}
	//否则 找到x所在的区间
	int mid=(l+r)/2;
	//在左区间 
	if(x<=mid) genxin(id*2,l,mid,x,v);
	//右区间 
	else genxin(id*2+1,mid+1,r,x,v);
	
	tr[id]=min(tr[id*2],tr[id*2+1]); 
	 
} 
//区间查询 l,r为当前区间,x,y为要查询的区间 id树结点编号 
int find(int id,int l,int r,int x,int y) 
{
	if(x>r||y<l) return 0;
	//该区间被包含在要查询的区间内 
	if(x<=l&&r<=y) return tr[id]; 
	int mid=(l+r)/2;
	int ans=INT_MAX;
	//与左半区间有交集 
	if(x<=mid)  ans=min(ans,find(id*2,l,mid,x,y)); //看求的是区间内的最小值还是最大值 
	//与右半区间有交集 
	else ans=min(ans,find(id*2+1,mid+1,r,x,y));
    return ans;  
}
int main()
{
	int m,n;
	cin>>m>>n;
	for(int i=1;i<=m;i++)
	cin>>a[i];
	build(1,1,m);
	while(n--)
	{
		int p,x,y;
		cin>>p>>x>>y;
		if(p==1)
		cout<<find(1,1,m,x,y)<<' ';
		else
		genxin(1,1,m,x,y);
	}
	return 0;
}

区间更新

建树

void build(int id,int l,int r)
{
	if(l==r) //左端点与右端点相等
	{
		sum[id]=a[l];
		return ;
	} 
	int mid=(l+r)/2;
	build(id*2,l,mid); //左子树 
	build(id*2+1,mid+1,r);//右子树
	//更新父节点 
	push_up(id);	
}

维护区间和

void push_up(int id)
{
	sum[id]=sum[id*2]+sum[id*2+1];//维护区间和 
}

懒标记

void push_down(int id,int l,int r)
{
		int mid=(l+r)/2;
		lazy[id*2]+=lazy[id]; //子节点进行标记
		lazy[id*2+1]+=lazy[id];
		sum[id*2]+=lazy[id]*(mid-l+1); //子节点的值发生变化
		sum[id*2+1]+=lazy[id]*(r-mid);
		lazy[id]=0;//清空lazy标记 
}

区间更新

//区间更新
void qjgx(int id,int l,int r,int x,int y,int v)
{
	if(x>r||y<l) return ; 
	//区间包含在被查找的区间 
	 if(l>=x&&r<=y)
	 {
	 	lazy[id]+=v;
	 	sum[id]+=v*(r-l+1);
	 	return ;
	 }
	 push_down(id,l,r);//更新下面的节点 
	 int mid=(l+r)/2;
	 //左半区间 
	qjgx(id*2,l,mid,x,y,v);
	 //右半区间 
	qjgx(id*2+1,mid+1,r,x,y,v);
	push_up(id);//子节点更新完成后,父节点也要更新 
} 

区间查询

//区间查询 l,r为当前区间,x,y为要查询的区间 id树结点编号 
int find(int id,int l,int r,int x,int y) 
{
	if(x>r||y<l) return 0;
	//该区间被包含在要查询的区间内 
	if(x<=l&&r<=y) return sum[id]; 
	push_down(id,l,r);//要查子节点了,下放 
	int mid=(l+r)/2;

    return find(id*2,l,mid,x,y)+find(id*2+1,mid+1,r,x,y);   
}

模板1

 

代码 

#include<bits/stdc++.h>
#define int long long 
using namespace std;

int sum[10000001],a[10000001],lazy[10000001];

void push_up(int id)
{
	sum[id]=sum[id*2]+sum[id*2+1];//维护区间和 
}


void push_down(int id,int l,int r)
{
		int mid=(l+r)/2;
		lazy[id*2]+=lazy[id]; //子节点进行标记
		lazy[id*2+1]+=lazy[id];
		sum[id*2]+=lazy[id]*(mid-l+1); //子节点的值发生变化
		sum[id*2+1]+=lazy[id]*(r-mid);
		lazy[id]=0;//清空lazy标记 
}
//建树 
void build(int id,int l,int r)
{
	if(l==r) //左端点与右端点相等
	{
		sum[id]=a[l];
		return ;
	} 
	int mid=(l+r)/2;
	build(id*2,l,mid); //左子树 
	build(id*2+1,mid+1,r);//右子树
	//更新父节点 
	push_up(id);	
}

//区间更新
void qjgx(int id,int l,int r,int x,int y,int v)
{
	if(x>r||y<l) return ; 
	//区间包含在被查找的区间 
	 if(l>=x&&r<=y)
	 {
	 	lazy[id]+=v;
	 	sum[id]+=v*(r-l+1);
	 	return ;
	 }
	 push_down(id,l,r);//更新下面的节点 
	 int mid=(l+r)/2;
	 qjgx(id*2,l,mid,x,y,v);
	 qjgx(id*2+1,mid+1,r,x,y,v);
	 push_up(id);//子节点更新完成后,父节点也要更新 
} 

//区间查询 l,r为当前区间,x,y为要查询的区间 id树结点编号 
int find(int id,int l,int r,int x,int y) 
{
	if(x>r||y<l) return 0;
	//该区间被包含在要查询的区间内 
	if(x<=l&&r<=y) return sum[id]; 
	push_down(id,l,r);//要查子节点了,下放 
	int mid=(l+r)/2;
	int ans=0;
	
    return find(id*2,l,mid,x,y)+find(id*2+1,mid+1,r,x,y);   
}

signed main()
{
    int n,m; 
	cin>>n>>m;
	for(int i=1;i<=n;i++)
	cin>>a[i];
	build(1,1,n); //先建树 
	while(m--)
	{
		int p,x,y,k;
		cin>>p>>x>>y;
		if(p==1)
		{
			cin>>k;
			qjgx(1,1,n,x,y,k);
		}
		else
		cout<<find(1,1,n,x,y)<<endl; 
	} 
	return 0;
}

 模板2

 代码

#include<bits/stdc++.h>
using namespace std;
const int N=1e6+9;
long long mod;
long long a[N];
struct s
{
	long long v,mul,add;
}st[N];
void build(int id,int l,int r)
{
	st[id].mul=1;
	st[id].add=0;
	if(l==r)
	{
		st[id].v=a[l];	
		st[id].v%=mod;
		return ;
	}
	else
	{
		int mid=(l+r)/2;
		build(id*2,l,mid);
		build(id*2+1,mid+1,r);
		st[id].v=st[id*2].v+st[id*2+1].v;
	}
	
}
void push_down(int id,int l,int r)
{
	int mid=(l+r)/2;
	st[id*2].v=(st[id*2].v*st[id].mul+st[id].add*(mid-l+1))%mod;
	st[id*2+1].v=(st[id*2+1].v*st[id].mul+st[id].add*(r-mid))%mod;
	
	st[id*2].mul=(st[id*2].mul*st[id].mul)%mod;
	st[id*2+1].mul=(st[id*2+1].mul*st[id].mul)%mod; 
	
	st[id*2].add=(st[id*2].add*st[id].mul+st[id].add)%mod;
	st[id*2+1].add=(st[id*2+1].add*st[id].mul+st[id].add)%mod;
	
	st[id].mul=1;
	st[id].add=0;
	return ;
}
void gx1(int id,int l,int r,int x,int y,int k)
{
	if(x>r||y<l) return;
	if(x<=l&&y>=r)
	{
		st[id].v=(st[id].v*k)%mod;
		st[id].mul=(st[id].mul*k)%mod;
		st[id].add=(st[id].add*k)%mod; 
		return ;
	}
	push_down(id,l,r);
	int mid=(l+r)/2;
    gx1(id*2,l,mid,x,y,k);
	gx1(id*2+1,mid+1,r,x,y,k);
	st[id].v=(st[id*2].v+st[id*2+1].v)%mod;
	return ;
}
void gx2(int id,int l,int r,int x,int y,int k)
{
	if(x>r||y<l) return;
	if(x<=l&&y>=r)
	{
		st[id].add=(st[id].add+k)%mod;
		st[id].v=(st[id].v+k*(r-l+1))%mod;
		return ;
	}
	push_down(id,l,r);
	int mid=(l+r)/2;
    gx2(id*2,l,mid,x,y,k);
	gx2(id*2+1,mid+1,r,x,y,k);
	st[id].v=(st[id*2].v+st[id*2+1].v)%mod;
	return ;
}
long long find(int id,int l,int r,int x,int y)
{
	if(x>r||y<l) return 0;
	if(x<=l&&y>=r)
	{
		return st[id].v;
	}
	push_down(id,l,r);
	int mid=(l+r)/2;
	return (find(id*2,l,mid,x,y)+find(id*2+1,mid+1,r,x,y))%mod;
}
int main()
{
	int n,q;
	cin>>n>>q>>mod;
	for(int i=1;i<=n;i++)
	cin>>a[i];
	build(1,1,n);
	while(q--)
	{
		//cout<<q<<endl;
		long long p,x,y,k;
		cin>>p;
		if(p==1)
		{
			cin>>x>>y>>k;
			gx1(1,1,n,x,y,k);
		}
		else if(p==2)
		{
			cin>>x>>y>>k;
			gx2(1,1,n,x,y,k);
		}
		else if(p==3)
		{
			cin>>x>>y;
			cout<<find(1,1,n,x,y)<<endl;
		}
	}
	return 0;
}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值