非递归版线段树模板

摘自这里

(0)定义:

#define maxn 100007
int A[maxn],n,N;//原数组,n为原数组元素个数 ,N为扩充元素个数 
int Sum[maxn<<2];//区间和 
int Add[maxn<<2];//懒标记 

(1)建树:

void Build(int n){
	//计算N的值 
	N=1;while(N < n+2) N <<= 1;
	//更新叶节点 
	for(int i=1;i<=n;++i) Sum[N+i]=A[i];//原数组下标+N=存储下标
	//更新非叶节点 
	for(int i=N-1;i>0;--i){
		//更新所有非叶节点的统计信息 
		Sum[i]=Sum[i<<1]+Sum[i<<1|1];
		//清空所有非叶节点的Add标记 
		Add[i]=0;
	}
} 

(2)点修改:

A[L]+=C
void Update(int L,int C)
{
	for(int s=N+L;s;s>>=1)
		Sum[s]+=C;
} 

(3)点修改下的区间查询:

求A[L…R]的和(点修改没有使用Add所以不需要考虑)

代码非常简洁,也不难理解,

s和t分别代表之前的论述中的左右蓝色节点,其余的代码根据之前的论述应该很容易看懂了。

st1 在s和t的父亲相同时值为0,终止循环。

两个if是判断s和t分别是左子节点还是右子节点,根据需要来计算Sum

int Query(int L,int R){
	int ANS=0;
	for(int s=N+L-1,t=N+R+1;s^t^1;s>>=1,t>>=1){
		if(~s&1) ANS+=Sum[s^1];
		if( t&1) ANS+=Sum[t^1];
	}
	return ANS;
} 

(4)区间修改:

A[L..R]+=C
void Update(int L,int R,int C){
	int s,t,Ln=0,Rn=0,x=1;
	//Ln:  s一路走来已经包含了几个数
	//Rn:  t一路走来已经包含了几个数
	//x:   本层每个节点包含几个数
	for(s=N+L-1,t=N+R+1;s^t^1;s>>=1,t>>=1,x<<=1){
		//更新Sum
		Sum[s]+=C*Ln;
		Sum[t]+=C*Rn;
		//处理Add
		if(~s&1) Add[s^1]+=C,Sum[s^1]+=C*x,Ln+=x;
		if( t&1) Add[t^1]+=C,Sum[t^1]+=C*x,Rn+=x;
	}
	//更新上层Sum
	for(;s;s>>=1,t>>=1){
		Sum[s]+=C*Ln;
		Sum[t]+=C*Rn;
	} 
}

(5)区间修改下的区间查询:

求A[L…R]的和

int Query(int L,int R){
	int s,t,Ln=0,Rn=0,x=1;
	int ANS=0;
	for(s=N+L-1,t=N+R+1;s^t^1;s>>=1,t>>=1,x<<=1){
		//根据标记更新 
		if(Add[s]) ANS+=Add[s]*Ln;
		if(Add[t]) ANS+=Add[t]*Rn;
		//常规求和 
		if(~s&1) ANS+=Sum[s^1],Ln+=x;
		if( t&1) ANS+=Sum[t^1],Rn+=x; 
	}
	//处理上层标记
	for(;s;s>>=1,t>>=1){
		ANS+=Add[s]*Ln;
		ANS+=Add[t]*Rn;
	}
	return ANS;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值