线段树

Posted aprincess

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了线段树相关的知识,希望对你有一定的参考价值。

1结构

线段树是一个平衡的二元树,所有叶子到根的距离最多只差1。令整个区间的长度为N,则其有N个叶节点,每个叶节点代表一个单位区间,每个内部结点代表的区间为其两个儿子代表区间的联集。

2基本操作

线段树所要提供的是查询一个区间内的资讯,并允许修改操作。要使用线段树,此资讯必须满足对于区间与位于区间内的一点,要可以由、求得。例如范围最值查询即符合此条件。

代码中, rt指的是root, 当前子树的根节点; l, r指的是当前子树所统计的区间利用完全二叉堆的性质来保存节点编号, 所以rt << 1是左子树的节点, rt << 1 | 1是右子树的节点在查询和成端更新操作中的L和R是指修改或者查询的区间

节点数据向上更新

将子节点的值更新到父节点。

/* 对于区间求和 */

void push_up(int rt)

tree[rt] = tree[rt << 1] + tree[rt << 1 | 1];

/* 对于区间求最大值 */

void push_up(int rt)

  tree[rt] = max(tree[rt << 1], tree[rt << 1 | 1]);

节点懒惰标记下推

对于区间求和, 原子数组值需要加上lazy标记乘以子树所统计的区间长度。len为父节点统计的区间长度, 则len - (len >> 1)为左子树区间长度, len >> 1为右子树区间长度。

void push_down(int rt, int len)

 tree[rt << 1] += lazy[rt] * (len - (len >> 1));

lazy[rt << 1] += lazy[rt];

tree[rt << 1 | 1] += lazy[rt] * (len >> 1);

lazy[rt << 1 | 1] += lazy[rt];

lazy[rt] = 0;

对于区间求最大值, 子树的值不需要乘以长度, 所以不需要传递参数len。

void push_down(int rt)

tree[rt << 1] += lazy[rt];

lazy[rt << 1] += lazy[rt];

tree[rt << 1 | 1] += lazy[rt];

lazy[rt << 1 | 1] += lazy[rt];

lazy[rt] = 0;

建树

新建一棵长度N的线段树。

#define lchild rt << 1, l, m

#define rchild rt << 1 | 1, m + 1, r

void build(int rt = 1, int l = 1, int r = N)

if (l == r)

std::cin >> tree[rt];

return;

int m = (l + r) >> 1;

build(lchild);

build(rchild);

push_up(rt);

更新

单点更新, 不需要用到lazy标记

#define lchild rt << 1, l, m

#define rchild rt << 1 | 1, m + 1, r

void update(int p, int delta, int rt = 1, int l = 1, int r = N)

if (l == r)

tree[rt] += delta;

return;

int m = (l + r) >> 1;

if (p <= m)

update(p, delta, lchild);

else update(p, delta, rchild);

push_up(rt);

成段更新, 需要用到lazy标记来提高时间效率

#define lchild rt << 1, l, m

#define rchild rt << 1 | 1, m + 1, r

void update(int L, int R, int delta, int rt = 1, int l = 1, int r = N)

if (L <= l && r <= R)

tree[rt] += delta * (r - l + 1);

lazy[rt] += delta;

return;

if (lazy[rt]) push_down(rt, r - l + 1);

int m = (l + r) >> 1;

if (L <= m) update(L, R, delta, lchild);

if (R > m) update(L, R, delta, rchild);

push_up(rt);

区间查询

#define lchild rt << 1, l, m

#define rchild rt << 1 | 1, m + 1, r

int query(int L, int R, int rt = 1, int l = 1, int r = N)

if (L <= l && r <= R)

return tree[rt];

 if (lazy[rt])

push_down(rt, r - l + 1);

 int m = (l + r) >> 1, ret = 0;

 if (L <= m) ret += query(L, R, lchild);

if (R > m)

ret += query(L, R, rchild);

return ret;

 

 

以上是关于线段树的主要内容,如果未能解决你的问题,请参考以下文章

一般线段树与权值线段树

详解权值线段树

权值线段树&&线段树合并

zkw线段树

权值线段树

#树# #线段树#