线段树模板

线段树定义:

struct node
{
	int l/*区间左边界*/,r/*区间右边界*/,sum/*区间元素之和*/,lazy/*懒惰标记,下文会提到*/;
	node(){l=r=sum=lazy=0;}//给每一个元素赋初值
}a[N];//N为总节点数
inline void update(int k)//更新节点k的sum
{
	a[k].sum=a[k*2].sum+a[k*2+1].sum;
	//很显然,一段区间的元素和等于它的子区间的元素和
}

初始化:

void build(int k/*当前节点的编号*/,int l/*当前区间的左边界*/,int r/*当前区间的右边界*/)
{
	a[k].l=l,a[k].r=r;
	if(l==r)//递归到叶节点
	{
		a[k].sum=number[l];//其中number数组为给定的初值
		return;
	}
	int mid=(l+r)/2;//计算左右子节点的边界
	build(k*2,l,mid);//递归到左儿子
	build(k*2+1,mid+1,r);//递归到右儿子
	update(k);//记得要用左右子区间的值更新该区间的值
}

单点修改:

void change(int k/*当前节点的编号*/,int x/*要修改节点的编号*/,int y/*要把编号为x的数字修改成y*/)
{
	if(a[k].l==a[k].r){a[k].sum=y;return;}
	//如果当前区间只包含一个元素,那么该元素一定就是我们要修改的。
	//由于该区间的sum一定等于编号为x的数字,所以直接修改sum就可以了。
	int mid=(a[k].l+a[k].r)/2;//计算下一层子区间的左右边界
	if(x<=mid) change(k*2,x,y);//递归到左儿子
	else change(k*2+1,x,y);//递归到右儿子
	update(k);//记得更新点k的值,感谢qq_36228735提出此错误
}

区间修改(有lazy标记)(区间加x):

void changeSegment(int k,int l,int r,int x)
//当前到了编号为k的节点,要把[l..r]区间中的所有元素的值+x
{
	if(a[k].l==l&&a[k].r==r)//如果找到了全部元素都要被修改的区间
	{
		a[k].sum+=(r-l+1)*x;
		//更新该区间的sum
		a[k].lazy+=x;return;
		//懒惰标记叠加
	}
	int mid=(a[k].l+a[k].r)/2;
	if(r<=mid) changeSegment(k*2,l,r,x);
	//如果被修改区间完全在左区间
	else if(l>mid) changeSegment(k*2+1,l,r,x);
	//如果被修改区间完全在右区间
	else changeSegment(k*2,l,mid,x),changeSegment(k*2+1,mid+1,r,x);
	//如果都不在,就要把修改区间分解成两块,分别往左右区间递归
	update(k);
	//记得更新点k的值
}

区间查询(求和):

int query(int k,int l,int r)
//当前到了编号为k的节点,查询[l..r]的和
{
	if(a[k].lazy) pushdown(k);
	//如果当前节点被打上了懒惰标记,那么就把这个标记下传,这一句其实也可以放在下一语句的后面
	if(a[k].l==l&&a[k].r==r) return a[k].sum;
	//如果当前区间就是询问区间,完全重合,那么显然可以直接返回
	int mid=(a[k].l+a[k].r)/2;
	if(r<=mid) return query(k*2,l,r);
	//如果询问区间包含在左子区间中
	if(l>mid) return query(k*2+1,l,r);
	//如果询问区间包含在右子区间中
	return query(k*2,l,mid)+query(k*2+1,mid+1,r);
	//如果询问区间跨越两个子区间
}

记得在查询之前下传标记!!!

下传标记:
 

void pushdown(int k)//将点k的懒惰标记下传
{
	if(a[k].l==a[k].r){a[k].lazy=0;return;}
	//如果节点k已经是叶节点了,没有子节点,那么标记就不用下传,直接删除就可以了
	a[k*2].sum=(a[k*2].r-a[k*2].l+1)*a[k].lazy;
	a[k*2+1].sum=(a[k*2+1].r-a[k*2+1].l+1)*a[k].lazy;
	//给k的子节点重新赋值
	a[k*2].lazy=a[k*2+1].lazy=a[k].lazy;
	//下传点k的标记
	a[k].lazy=0;//记得清空点k的标记
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

deebcjrb

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值