线段树模板

线段树详解

建树:

int sum[N<<2],add[N<<2];//sum求和,add为懒惰标记
int a[N],n;//存原数组数据下标[1,n]
//PushUp函数更新节点信息(这里是求和):
void pushup(int p)
{
    sum[p]=sum[p*2]+sum[p*2+1];
}
//Build函数建树:
void build(int l,int r,int p)//l,r表示当前节点区间,rt表示当前节点编号
{
	if(l==r)//若到达叶节点
    {
		sum[p]=a[l];//储存数组值
		return ;
	}
	int mid=(l+r)/2;//左右递归
	build(l,mid,p*2);
	build(mid+1,r,p*2+1);//更新信息
	pushup(p);
}

点修改:

//点修改:
void updatedian(int L,int c,int l,int r,int p)//L表示要修改的点,C表示要修改的值,l,r表示当前节点区间,rt表示当前节点编号
{
	if(l==r)//到叶节点,修改
    {
		sum[p]+=c;
		return ;
	}
	int mid=(l+r)/2;
	if(L<=mid)//根据条件判断往左子树调用还是往右
	{
	    updatedian(L,c,l,mid,p*2);
    }
	else
	{
	    updatedian(L,c,mid+1,r,p*2+1);
	}
	pushup(p);//子节点更新了,所以本节点也需要更新信息
}

区间修改:

//修改时的下推标记函数
//下推标记函数:
void pushdown(int p,int ln,int rn)//ln,rn为左子树,右子树的数字数量。
{
	if(add[p])//该点的add值不为0,说明子区间的Sum需要根据Add的值来调整
    {
		//下推标记
		add[p*2]+=add[p];
		add[p*2+1]+=add[p];
		//修改子节点的Sum使之与对应的Add相对应
		sum[p*2]+=add[p]*ln;
		sum[p*2+1]+=add[p]*rn;
		add[p]=0;//清除本节点标记
	}
}
void updatequjian(int L,int R,int c,int l,int r,int p)//L,R表示操作区间,l,r表示当前节点区间,rt表示当前节点编号
{
	if(L<=l&&r<=R)//如果本区间完全在操作区间[L,R]以内
    {
		sum[p]+=c*(r-l+1);//更新数字和,向上保持正确
		add[p]+=c;//增加Add标记,表示本区间的Sum正确,子区间的Sum仍需要根据Add的值来调整
		return ;
	}
	int mid=(l+r)/2;
	pushdown(p,mid-l+1,r-mid);//下推标记
	if(L<=mid)//这里判断左右子树跟[L,R]有无交集,有交集才递归
	{
	    updatequjian(L,R,c,l,mid,p*2);
	}
	if(R>mid)
	{
	    updatequjian(L,R,c,mid+1,r,p*2+1);
	}
	pushup(p);//更新本节点信息
}

区间查询:

int query(int L,int R,int l,int r,int p)//L,R表示操作区间,l,r表示当前节点区间,rt表示当前节点编号
{
	if(L<=l&&r<=R)//在区间内,直接返回
    {
		return sum[p];
	}
	int mid=(l+r)/2;//下推标记,否则Sum可能不正确
	pushdown(p,mid-l+1,r-mid);
	int ans=0;//累计答案
	if(L<=mid)
	{
	    ans+=query(L,R,l,mid,p*2);
    }
	if(R>mid)
    {
        ans+=query(L,R,mid+1,r,p*2+1);
    }
	return ans;
}

使用:

signed main()
{
	build(1,n,1); //建树
	update(L,C,1,n,1);//点修改
	update(L,R,C,1,n,1);//区间修改
	int a=query(L,R,1,n,1);//区间查询
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值