码迷,mamicode.com
首页 > 其他好文 > 详细

线段树

时间:2019-10-05 10:25:39      阅读:77      评论:0      收藏:0      [点我收藏+]

标签:else   父节点   代码   时间   节点数据   size   strong   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;

}

 

 

线段树

标签:else   父节点   代码   时间   节点数据   size   strong   com   保存   

原文地址:https://www.cnblogs.com/aprincess/p/11623867.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!