线段树定义理解与模板

19 篇文章 0 订阅
6 篇文章 0 订阅

超级开心,今天的屁屁踢有详细注释!(虽然这仍然弥补不了我抄错模板的事实。。。)

今天七夕,虽然我不过节,但是让我应景的来一句土味情话(对每一个我A出来的题):我能想到最浪漫的事,就是看着你由wa到T,再T又T还T,TTTTTTT,最终成为那抹绿色靓丽的AC(撒花~~~)

废话不再说,附上具有详细注释的带有我个人理解的模板代码!

建树模板

void build(int rt,int l,int r)//rt为当前建立的节点编号(下标),l、r为rt维护的区间的左右端点
{
	if(l ==r)//当该节点为叶子节点时 
	{
		sum[rt]=a[l];//a为原序列,直接赋该值给叶子节点 
		return;
	}
	int m = (l+r)>>1;//m为该节点左孩子区间的右端点
	build(rt<<1,l,m);//rt<<1为该节点左孩子下标 
	build(rt<<1|1,m+1,r); //rt<<1|1 == rt*2+1 为该节点右孩子编号
	push_up(rt);//该函数作用是将子树信息向上更新,更新到父节点上 
} 

void push_up(int rt)
{
	sum[rt]=sum[rt<<1]+sum[rt<<1|1];
} //以更新和为例 

build(1,1,n);//函数调用 

点修改

void update(int rt,int l,int r,int p,int v)//rt为正在遍历的节点下标,l,r为rt的区间左右端点,p为需要更改的节点下标,v为修改值(此处以v为增加的值为例)
{
	if(l==r)//如果为叶子节点,直接更新 
	{
		sum[rt]+=v;
		return;
	}
	int m=(l+r)>>1;//m为rt节点的左右孩子的区间分界线 
	if(p <= m)//当需要更新的p节点在左孩子所维护区间 
	{
		update(rt<<1,l,m,p,v);
	}
	else//当需要更新的p节点在右孩子所维护区间
	{
		update(rt<<1|1,m+1,r,p,v);
	}
	push_up(rt);//将子树信息更新到父节点 
} 

update(1,l,n,p,v)//函数调用 

单点修改的区间查询

int query(int rt,int l,int r,int ll,int rr)// rt为正在遍历到的节点下标,l,r为rt的区间端点,ll,rr为查询的区间端点 
{
	if(ll<=l && r<=rr)//当查询区间位于rt维护的区间内时,直接返回 
	{
		return sum[rt];
	}
	int res=0;//返回值变量,初始化视情况而定 
	int m=(l+r)>>1;//m为rt左右孩子分界线 
	if(ll<=m)//如果查询区间与左孩子有交集 
	{
		res+=query(rt<<1,l,m,ll,rr);//继续在左孩子区间内查询 
	}
	if(rr>m)//如果查询区间与右孩子有交集 
	{
		res+=query(rt<<1|1,m+1,r,ll,rr);//继续在右孩子区间内查询 
	}
	return res;
} 

query(1,l,r,ll,rr)//函数调用 

区间修改

我们引入了变量—懒标记(lazy),顾名思义懒标记懒标记,就是我们懒得修改从而打的标记,我们如何懒且能使答案正确呢,我们知道我们只需要修改区间(l,r),我们需要修改的点维护的区间一定在需要修改的区间内,因此如果一个节点再需要修改的区间内,我们可以直接修改一个大区间相对应的值,并不需要修改它的子节点,等我们需要单独提出该子节点的信息的时候在下传这个懒标记并修改这个子节点,这样我们可以在O(log n)的情况下完成一整个区间的修改了,对于查找区间相似的我们只需要查找完全在修改区间内的数值即可.

void update(int rt,int l,int r,int ll,int rr,int v)//rt为正在遍历到的节点下标,l,r为rt的区间端点,ll,rr为查询的区间端点 ,v为区间修改的值(以区间每个数都加v为例) 
{
	if(ll<=l && rr>=r)//如果需要更新的区间包含于rt所维护的区间内时 
	{
		lazy[rt]+=v;//添加lazy标记 
		sum[rt]+=v*(r-l+1);//只改这个最上层的大区间 
		return;
	}
	push_down(rt,l,r);//查询lazy标记,更新子树 
	int m=(l+r) >> 1;//m为子树的左右分界线 
	if(ll<=m) update(rt<<1,l,m,ll,rr,v);//如果左子树和需要更新的区间交集非空 ,更新左子集 
	if(rr>m) update(rt<<1|1,m+1,r,ll,rr,v);//如果右子树和需要更新的区间交集非空 ,更新右子集
	push_up(rt);//更新父节点 
} 

void push_down(int rt,int l,int r)//更新查询到的子树的值 
{
	if(lazy[rt])//如果有lazy标记 
	{
		int m=(l+r)>>1;//m为左右子树分界线 
		lazy[rt<<1] += lazy[rt];//更新左子树lazy值 
		lazy[rt<<1|1] += lazy[rt];//更新右子树lazy值 
		sum[rt<<1] += lazy[rt]*(m-l+1);//左子树的和加上lazy值 
		sum[rt<<1|1] += lazy[rt]*(r-m);//右子树的和加上lazy值 
		lazy[rt]=0;//该节点lazy已下传子树,清空 
	}
} 

update(1,l,r,ll,rr,val)//函数调用 

区间修改的区间查询

int query(int rt,int l,int r,int ll,int rr)// rt为正在遍历到的节点下标,l,r为rt的区间端点,ll,rr为查询的区间端点 
{
	if(ll<=l && r<=rr)//当查询区间位于rt维护的区间内时,直接返回 
	{
		return sum[rt];
	}
	push_down(rt,l,r);//每次访问都检查lazy标记 
	int m=(l+r)>>1;//m为rt左右孩子分界线 
	long long res=0;//返回值变量,初始化视情况而定 
	if(ll<=m)//如果查询区间与左孩子有交集 
	{
		res+=query(rt<<1,l,m,ll,rr);//继续在左孩子区间内查询 
	}
	if(rr>m)//如果查询区间与右孩子有交集 
	{
		res+=query(rt<<1|1,m+1,r,ll,rr);//继续在右孩子区间内查询 
	}
	return res;
} 

query(1,1,n,ll,rr)//函数调用 

总模板:

#include <stdio.h>
#include <iostream>
using namespace std;
const int maxn=1000010;
int sum[maxn*4];
int lazy[maxn*4]; 
void build(int rt,int l,int r)//建树 
{
	if(l==r)
	{
		sum[rt] = a[l];
		return;
	}
	int m = (l+r)>>1;
	build(rt<<1,l,m);
	build(rt<<1|1,m+1,r);
	push_up(rt);
}
void push_up(int rt)
{
	sum[rt] = sum[rt<<1]+sum[rt<<1|1];
}
void update(int rt,int l,int r,int p,int v)//点修改 
{
	if(l == r)
	{
		sum[rt] += v;
		return;
	}
	int m = (l+r) >> 1;
	if(p <= m)
	{
		update(rt<<1,l,m,p,v);
	}
	else
	{
		update(rt<<1|1,m+1,r,p,v);
	}
	push_up(rt);
}

void update(int rt,int l,int r,int ll,int rr,int v)//区间修改 
{
	if(ll<=l && r<=rr)
	{
		lazy[rt]+=v;
		sum[rt]+=v*(r-l+1);
		return;
	}
	push_down(rt,l,r);
	int m = (l+r)>>1;
	if(ll<=m)
	{
		update(rt<<1,l,m,ll,rr,v);
	}
	if(rr>m)
	{
		update(rt<<1|1,m+1,r,ll,rr,v);
	}
	push_up(rt);
 } 
 void push_down(int rt,int l,int r)
 {
 	if(lazy[rt])
 	{
 		int m=(l+r)>>1;
 		lazy[rt<<1]+=lazy[rt];
 		lazy[rt<<1|1]+=lazy[rt];
 		sum[rt<<1]+=lazy[rt]*(m-l+1);
 		sum[rt<<1|1]+=lazy[rt]*(r-m);
 		lazy[rt]=0;	
	 }
 }
int query(int rt,int l,int r,int ll,int rr)//区间查询 
{
	if(ll<=l && r<=rr)
	{
		return sum[rt];
	}
	push_down(rt,l,r);
	int m = (l+r)>>1;
	long long res=0;
	if(ll<=m) res+=query(rt<<1,l,m,ll,rr);
	if(rr>m) res+=query(rt<<1|1,m+1,r,ll,rr);
	return res;
}
int main()
{
        build(1, n, 1);
    	query(j, j, 1, n, 1)//单点查询 
    	query(i, j, 1, n, 1)//区间查询 
 } 

最后来张萌图,(*^__^*) 嘻嘻……

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值