• 博客园logo
  • 会员
  • 众包
  • 新闻
  • 博问
  • 闪存
  • 赞助商
  • HarmonyOS
  • Chat2DB
    • 搜索
      所有博客
    • 搜索
      当前博客
  • 写随笔 我的博客 短消息 简洁模式
    用户头像
    我的博客 我的园子 账号设置 会员中心 简洁模式 ... 退出登录
    注册 登录
jacklee404
Never Stop!
博客园    首页    新随笔    联系   管理    订阅  订阅
线段树笔记

创建线段树O(n)

原数据的大小为N(例如维护区间合时,原数组总数N),则线段树要开4 * N

int n, w[N];
struct node{
	int l, r, sum;
}tr[N * 4];
void build(int p, int l, int r){
	tr[p] = {l, r, w[l]}; // leaf node l == r sum = w[l];
	if (l == r) return;
	int m = l + r >> 1;
	build(lc, l , m); // recursive rigth and left
	build(rc, m + 1, r);
	tr[p].sum = tr[lc].sum + tr[rc].sum; // up make w[l] plus w[r]
}

点修改O(logn)

从根结点进入,递归找到叶子结点[x, x], 把该节点的值增加x。从下往上更新祖先节点。

void update(int p, int x, int k){
	if (tr[p].l == x && tr[p].r == x){ // if leaf node return
		tr[p].sum += k;
		return;
	}
	int m = tr[p].l + tr[p].r >> 1; // update the road from root to leaf node.
	if (x <= m) update(lc, x, k);
	if (x > m) update(rc, x, k);
	tr[p].sum = tr[lc].sum + tr[rc].sum;
}

区间查询O(logn)

​ 拆分与拼凑的思想。例如,查询区间 [4, 9] 可以拆分成 [4, 5], [6, 8], [9, 9] 通过合并这三个区间的答案求查询答案。

从根节点进入,递归执行一下过程:

1. 若查询区间 `[x, y]` 完全覆盖当前节点区间则立即回溯,并返回该节点的数值
1. 因为线段树保证了每个区间都不会有重叠,所以我们递归遍历左右节点,如果查询区间包含在内则递归访问相应的左右子树	
int query(int p, int x, int y){
	if (x <= tr[p].l && tr[p].r <= y)
		return tr[p].sum;
	int m = tr[p].l + tr[p].r >> 1;
	int sum = 0;
	if (x <= m) sum += query(lc, x, y);
	if (y > m) sum += query(rc, x, y);
	return sum;
}

区间修改 (lazy tag)O(logn)

​ 如果进行单点修改的话,那么每次都要把[4, 5]的所有叶子节点修改,然后再向上更新祖先节点,时间复杂度时O(n)的。

​ 优化:

我们做*** 懒惰修改, 当[x, y] 完全覆盖节点区间[a, b]时, 先修改区间的sum值,再打上一个“懒标记”,先修改该区间的sum数值,在打赏一个懒标记,然后立即返回。等下次需要的时候,在下传“懒标记”,然后立即返回。等下次需要时,在下传“懒标记”。这样,可以把每次修改和查询时间都控制到O(logn)***

posted on 2022-07-26 11:27  Jack404  阅读(10)  评论(0)    收藏  举报
刷新页面返回顶部
博客园  ©  2004-2025
浙公网安备 33010602011771号 浙ICP备2021040463号-3