线段树(附详细解释)

线段树

#define maxn 1000005
typedef long long ll;
ll a[maxn]; //这数组是读入数据,存储数列 
ll tree[maxn * 4];
/*tree存储线段树,设一个非叶子节点所在的下标是i,
那么左子树下标就是2i,右子树下标就是2i+1 */
ll lzy[maxn * 4];
//lzy是懒标记,用来记录一个区间要加上多少值 
int n, m, opt, ans = 0;
//===================构造线段树======================
inline void refresh_tree(const int u) //用来更新tree数组 
{
	tree[u] = tree[u << 1] + tree[(u << 1) + 1];
	//区间[1,3]和区间[4,4]的和加起来构成区间[1,4]的和 
}
inline void made_tree(const int u, int L, int R) //构造线段树 
{
	if(L == R) //如果区间长度就是1的话,给tree赋值 
	{
		tree[u] = a[L];
		return;
	}
	int mid = (L + R) >> 1; //否则遍历左右子树	
	made_tree(u << 1, L, mid); //左子树 
	made_tree((u << 1) + 1, mid+1, R); //右子树 
	refresh_tree(u); //更新结点 
}
//===================单点查询与修改======================
inline ll find_point(int u, int L, int R, int p) //查询p 
{
	if(L == R) //如果已经找到了 
		return tree[u]; //直接返回 
	int mid = (L + R) >> 1; //取中间值 
	if(p <= mid) //如果要查询的数在[L,mid]中间 
		return find_point(u << 1, L, mid, p);
	else
		return find_point((u << 1) + 1, mid+1, R, p);
}
inline void change_point(int u, int L, int R, int p, ll x)
{ //修改tree[u]成x 
	if(L == R)
	{
		tree[u] = x; //到达叶节点直接赋值并返回 
		return ;
	}
	else
	{
		int mid = (L + R) >> 1;
		if(p <= mid)
			change_point(u << 1, L, mid, p, x);
		else
			change_point((u << 1) + 1, mid + 1, R, p, x);
		refresh_tree(u);
	}
}
//===================区间的查询======================
inline bool In_range(int L, int R, int l, int r)
{ //判断[l,r]是否包含[L,R] 
	return (R <= r) && (L >= l);
}
inline bool Out_of_range(int L, int R, int l, int r)
{ //判断[l,r]∩[L,R] 是否等于空集 
	return (R < l) || (L > r);
}
inline ll find_range(int u, int L, int R, int l, int r)
{ //求区间[l,r]的和 
	if(In_range(L, R, l, r)) //包含关系,直接返回 
		return tree[u];
	else if(!Out_of_range(L, R, l, r))
	{
		int mid = (L + R) >> 1;
		return find_range(u << 1, L, mid, l, r) + find_range((u << 1) + 1, mid + 1, R, l, r);
	}
	else return 0;
}
//===================区间的移改======================
inline void maketag(int u, int len, ll x)
{
	lzy[u] += x; //修改结点的延迟标记 
	tree[u] += len * x; //修改结点的区间和 
}
inline void push_down(int u, int L, int R)
{
	int mid = (L + R) >> 1;
	maketag(u << 1, mid - L + 1, lzy[u]); //遍历左子树并加上标记 
	maketag((u << 1) + 1, R - mid, lzy[u]); //遍历右子树并加上标记
	lzy[u] = 0;
}
inline ll work(int u, int L, int R, int l, int r) //返回[l,r]的区间和 2 
{
	int mid = (L + R) / 2;
	if(In_range(L, R, l, r)) //若完全包含 
		return tree[u]; //直接返回区间和 
	else if(!Out_of_range(L, R, l, r))
	{
		push_down(u, l, R); //必须现将当前节点标记后再查询 
		return work(u << 1, L, mid, l, r) + work((u << 1) + 1, mid + 1, R, l, r);
	}
	else return 0; //无交则返回0 
}
inline void update(int u, int L, int R, int l, int r, ll x)
{
	if(In_range(L, R, l, r))
		maketag(u, R - L + 1, x); //直接打标记 
	else if(!Out_of_range(L, R, l, r))
	{
		int mid = (L + R) / 2;
		push_down(u, L, R); //必须现将结点的标记往下传,再修改下面的结点 
		update(u << 1, L, mid, l, r, x);
		update((u << 1) + 1, mid + 1, R, l, r, x);
		refresh_tree(u);
	}
}
  • 12
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值