线段树的一些细节区别 以区间和为例

线段树的几种流派 以区间和为例

节点内存储L、R

  • 需要很多存储空间,debug方便,写起来较下面一种方法慢

节点不内存储L、R

  • 节省大量存储空间,但是debug困难,并且push_down()不能写成函数(没有区间长度信息)

检测区间匹配

包含时就操作

直接暴力分割,在函数开头检测,不行就return。

完全匹配才操作

三种条件分割(左,右,中间),只有在中间才分割。

区间更新

更新时,lazy加入sum

  • 使用push_up()来更新
  • update时,需要push_down()

以下是示例代码(部分代码,poj3468,AC)

#define ti tree[i];
struct node
{
	int l;
	int r;
	long long sum;
	long long lazy;//此处的lazy加入sum
	int len()const { return r - l + 1; }
	void print()const { printf("%d %d %lld %lld\n", l, r, sum, lazy); }
};

const int maxn = 100000 + 10;
node tree[maxn * 4];
int arr[maxn];
#define ti tree[i]

void push_up(int i)
{
	tree[i].sum = tree[i * 2].sum + tree[i * 2 + 1].sum;
}

void push_down(int i)
{
	if (tree[i].l != tree[i].r)
	{
		tree[i * 2].lazy += tree[i].lazy;
		tree[i * 2 + 1].lazy += tree[i].lazy;
		tree[i * 2].sum += tree[i].lazy*tree[i * 2].len();
		tree[i * 2 + 1].sum += tree[i].lazy*tree[i * 2 + 1].len();
		tree[i].lazy = 0;
	}
}

void build(int i, int l, int r)
{
	ti.l = l;
	ti.r = r;
	if (l == r)
	{
		ti.sum = arr[r - 1];
		ti.lazy = 0;
		return;
	}
	int mid = (l + r) / 2;
	build(i * 2, l, mid);
	build(i * 2 + 1, mid + 1, r);
	push_up(i);
}

void update(int i, int ul, int ur, int val)
{
	if (ti.lazy)
		push_down(i);

	if (ti.l == ul && ti.r == ur)
	{
		ti.lazy += val;
		ti.sum += val * ti.len();
		return;
	}

	int mid = (ti.l + ti.r) / 2;
	if (ur <= mid)
	{
		update(i * 2, ul, ur, val);
		push_up(i);
	}
	else if (ul > mid)
	{
		update(i * 2 + 1, ul, ur, val);
		push_up(i);
	}
	else
	{
		update(i * 2, ul, mid, val);
		update(i * 2 + 1, mid + 1, ur, val);
		push_up(i);
	}
}

long long query(int i, int ql, int qr)
{
	if (ti.lazy)
		push_down(i);

	if (ti.l == ql && ti.r == qr)
	{
		return ti.sum;
	}

	int mid = (ti.l + ti.r) / 2;
	if (qr <= mid)
	{
		return query(i * 2, ql, qr);
	}
	else if (ql > mid)
	{
		return query(i * 2 + 1, ql, qr);
	}
	else
	{
		return query(i * 2, ql, mid) + query(i * 2 + 1, mid + 1, qr);
	}
}

void print(int i, int l, int r)
{
	ti.print();
	if (l != r)
	{
		int mid = (l + r) / 2;
		print(i * 2, l, mid);
		print(i * 2 + 1, mid + 1, r);
	}
}

更新时,lazy不加入sum

  • 更新时,途径节点的sum直接加上此区间包含的更新区间应加的值。(或者是在返回值里传递加上lazy后的sum值)
  • 如果更新没有顺序没有影响,可以update时不更新,留在query时再更新。还可以不更新,query时加上lazy对应区段加上的值。

顺序有影响的例子:区间涂色
顺序没有影响的例子:区间和

以下是示例代码(部分代码,poj3468,AC)

#define ti tree[i];
struct node
{
	int l;
	int r;
	long long sum;
	long long lazy;//此处的lazy不加入sum
	int len()const { return r - l + 1; }
	void print()const { printf("%d %d %lld %lld\n", l, r, sum, lazy); }
};

const int maxn = 100000 + 10;
node tree[maxn * 4];
int arr[maxn];
#define ti tree[i]

void push_up(int i)
{
	tree[i].sum = tree[i * 2].sum + tree[i * 2 + 1].sum;
}

void push_down(int i)
{
	if (tree[i].l != tree[i].r)
	{
		tree[i].sum += tree[i].lazy*tree[i].len();
		tree[i * 2].lazy += tree[i].lazy;
		tree[i * 2 + 1].lazy += tree[i].lazy;
		tree[i].lazy = 0;
	}
}

void build(int i, int l, int r)
{
	ti.l = l;
	ti.r = r;
	if (l == r)
	{
		ti.sum = arr[r - 1];
		ti.lazy = 0;
		return;
	}
	int mid = (l + r) / 2;
	build(i * 2, l, mid);
	build(i * 2 + 1, mid + 1, r);
	push_up(i);
}

void update(int i, int ul, int ur, int val)
{
	if (ti.lazy)
		push_down(i);

	if (ti.l == ul && ti.r == ur)
	{
		ti.lazy += val;
		return;
	}
	else
	{
		ti.sum += val * (ur - ul + 1);
	}

	int mid = (ti.l + ti.r) / 2;
	if (ur <= mid)
	{
		update(i * 2, ul, ur, val);
	}
	else if (ul > mid)
	{
		update(i * 2 + 1, ul, ur, val);
	}
	else
	{
		update(i * 2, ul, mid, val);
		update(i * 2 + 1, mid + 1, ur, val);
	}
}

long long query(int i, int ql, int qr)
{
	if (ti.lazy)
		push_down(i);

	if (ti.l == ql && ti.r == qr)
	{
		return ti.sum + ti.lazy*ti.len();
	}

	int mid = (ti.l + ti.r) / 2;
	if (qr <= mid)
	{
		return query(i * 2, ql, qr);
	}
	else if (ql > mid)
	{
		return query(i * 2 + 1, ql, qr);
	}
	else
	{
		return query(i * 2, ql, mid) + query(i * 2 + 1, mid + 1, qr);
	}
}

void print(int i, int l, int r)
{
	ti.print();
	if (l != r)
	{
		int mid = (l + r) / 2;
		print(i * 2, l, mid);
		print(i * 2 + 1, mid + 1, r);
	}
}
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值