【线段树】

线段树

之前对线段树不是很明白

今天自己重新手写了一份线段树

#include<bits/stdc++.h>
#define int long long
using namespace std;

const int maxn = 1e6 + 10;

struct Node {
	int l, r, sum, lz;
}T[maxn << 2];


void push_up(int o) {
	T[o].sum = T[o << 1].sum + T[o << 1 | 1].sum;
}

void build(int o, int L, int R) {
	T[o] = Node{ L,R,0,0 };
	if (L == R) {
		cin >> T[o].sum;
		return;
	}
	int mid = L + R >> 1;
	build(o << 1, L, mid);
	build(o << 1 | 1, mid + 1, R);
	push_up(o);
}


void push_down(int o) {
	if (!T[o].lz)return;

	T[o << 1].lz += T[o].lz;
	T[o << 1 | 1].lz += T[o].lz;

	T[o << 1].sum += (T[o << 1].r - T[o << 1].l + 1) * T[o].lz;
	T[o << 1 | 1].sum += (T[o << 1 | 1].r - T[o << 1 | 1].l + 1) * T[o].lz;

	T[o].lz = 0;
}

void updt(int o, int l, int r, int z) {
	if (l <= T[o].l && T[o].r <= r) {
		T[o].lz += z;
		T[o].sum += z * (T[o].r - T[o].l + 1);
		return;
	}
	push_down(o);
	int mid = T[o].l + T[o].r >> 1;
	if (l <= mid) updt(o << 1, l, r, z);
	if (r > mid) updt(o << 1 | 1, l, r, z);
	push_up(o);
}

int query(int o, int l, int r) {
	if (l <= T[o].l && T[o].r <= r) {
		return T[o].sum;
	}
	push_down(o);
	int sum = 0, mid = T[o].l + T[o].r >> 1;
	if (l <= mid)sum += query(o << 1, l, r);
	if (r > mid)sum += query(o << 1 | 1, l, r);
	return sum;
}

要理解这份最原始的线段树代码,首先要明确的是

  • (Lazy) 的含义以及作用范围
  • (sum) 的含义以及作用范围

(Lazy) 作用范围是当前节点的所有子节点,不包括自己在内

(sum) 的含义是当前子树之和,包含当前节点在内

再来看 (push\_down)(push\_up) 两个函数

void push_up(int o) {
	T[o].sum = T[o << 1].sum + T[o << 1 | 1].sum;
}

push_up 是在回溯到根节点的时候,更新当前的 (sum)

void push_down(int o) {
	if (!T[o].lz)return;
	//传递标记
	T[o << 1].lz += T[o].lz;
	T[o << 1 | 1].lz += T[o].lz;
	//作用子节点
	T[o << 1].sum += (T[o << 1].r - T[o << 1].l + 1) * T[o].lz;
	T[o << 1 | 1].sum += (T[o << 1 | 1].r - T[o << 1 | 1].l + 1) * T[o].lz;

	T[o].lz = 0;
}

push_down 是将 (lazy) 标记往下传递,由于 (lazy) 标记的作用范围是所有子节点,所以将标记传递给子节点后,还要将标记作用在两个子节点上。

在update的时候,路径上的所有点都被push_down过,update完后,根节点的sum值就是全部的和。

原文地址:https://www.cnblogs.com/sduwh/p/13650327.html