线段树的几种流派 以区间和为例
节点内存储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);
}
}