Segment Tree

線斷

建北電資小社課 演算法

Segment Tree

線段樹

  • 225班
  • 電子計算機研習社_學術長
  • 綽號807

這我

這建北電資

線段樹

線段樹

線段樹

怎麼分區間?

從中間切一半

1

5

5

8

4

3

7

2

6

13

7

9

19

16

35

nn 為2的冪次

nn 代表資料長度

如果資料長度不是2的冪次

1

5

8

4

3

7

2

1

13

7

9

14

16

30

這樣也行

nn 非2的冪次

nn 代表資料長度

  • 建樹 : O(nlog(n))O(n log(n))O(n)O(n)
  • 查詢 : O(log(n))O(log(n))
  • 修改 : O(log(n))O(log(n))

複雜度

複雜度比較

線段樹 前綴和 差分
建表
 
區間查詢
單點修改
區間修改

O(n)O(n)

O(n)O(n)

O(log(n))O(log(n))

O(1)O(1)

O(n)O(n)

O(log(n))O(log(n))

O(n)O(n)

O(1)O(1)

O(log(n))O(log(n))

O(n)O(n)

O(1)O(1)

存樹

  • 用 struct 存
    • 左子節點指標
    • 右子節點指標

指標型存法

struct node{
    int value;
    node *left, *right;
};
  • 開 4 倍大的陣列
  • 以節點 xx
    • 左子節點位置 x×2x \times 2
    • 右子節點位置 x×2+1x \times 2 + 1

陣列型存法 (偽指標型)

int n;
vector<int> data(n*4,0);

註: nn 代表原資料長度

4 倍 哪來的

nn 為2的冪次

註: nn 代表原資料長度

  • 最下層有 nn 個 (節點)
  • 上層有 n÷2n \div 2
  • 上上層有 n÷2÷2n \div 2 \div 2
  • 上上上層有 n÷2÷2÷2n \div 2 \div 2 \div 2

. . .

nn 為2的冪次

註: nn 代表原資料長度

  • log2n+1log_2 n+1
  • 需要 2log2n+11212n\frac{2^{log_2 n+1}-1}{2-1} \approx 2n 個 (節點)

nn 不是 2的冪次

註: nn 代表原資料長度

  • nn2n2n 間有一個 nn 的冪次
  • 他需要的空間小於 2n×2=4n2n \times 2 = 4n
  • 我不會證明,你可以接受就好
  • 2 倍大的陣列
  • 以節點 xx
    • 左子節點位置 x×2x \times 2
    • 右子節點位置 x×2+1x \times 2 + 1

迭代式線段樹陣列型存法

int n;
vector<int> data(n*2,0);

註: nn 代表原資料長度

2 倍 哪來的

有點複雜等等再說

建樹

Create

  • 遞迴將左右子節點計算完
  • 結和左右子節點值 (pull)
  • 遞迴完就結束了

建樹 遞迴陣列型

線段樹 建樹 code

#include <bits/stdc++.h>
#define int long long
using namespace std;
struct SegmentTree {
	int n;
	vector<int> nodes;
	SegmentTree()
		: n(0) {
		nodes.clear();
	}
	SegmentTree(vector<int> &data)
		: n(data.size()) {
		nodes.resize(n * 4);
		build(data, 0, n);
	}
	void build(const vector<int> &data, int l, int r, int it = 1) {
		if (r - l == 1) {
			nodes[it] = data[l];
			return;
		}
		int mid = (l + r) / 2;
		build(data, l, mid, it * 2);
		build(data, mid, r, it * 2 + 1);
		nodes[it] = nodes[it * 2] + nodes[it * 2 + 1];
		return;
	}
};
# PRESENTING CODE

初始化 nodes 大小

宣告 變數

到葉節點 複製值進去

取得中間值 區分左右子節點的區間

左區間在 x×2x \times 2

右區間在 x×2+1x \times 2 +1

遞迴計算子節點值

結合左右子節點值 (pull)

遞迴函式

還沒到葉節點

變數解釋

nn 資料長度

nodesnodes 節點值

datadata 原始資料

ll rr 左右邊界

midmid 中介: 區分左右子區間

itit 偽指標: 指向結點在nodesnodes的位置

註: 皆為左閉右開 0-base

  • 少於4n4n 個節點
  • 每個節點只跑一次
  • 複雜度: O(n)O(n)

複雜度分析

查詢

Read

  • 遞迴下去做
  • 三種可能
    • 完全在左
    • 完全在右
    • 左右都有

查詢

線段樹 查詢 code

#include <bits/stdc++.h>
#define int long long
using namespace std;
struct SegmentTree {
	int n;
	vector<int> nodes;
	SegmentTree()
		: n(0) {
		nodes.clear();
	}
	SegmentTree(vector<int> &data)
		: n(data.size()) {
		nodes.resize(n * 4);
		build(data, 0, n);
	}
	void build(const vector<int> &data, int l, int r, int it = 1) {
		if (r - l == 1) {
			nodes[it] = data[l];
			return;
		}
		int mid = (l + r) / 2;
		build(data, l, mid, it * 2);
		build(data, mid, r, it * 2 + 1);
		nodes[it] = nodes[it * 2] + nodes[it * 2 + 1];
		return;
	}
	int query(int queryL, int queryR, int l, int r, int it = 1) {
		if (queryL == l && queryR == r) return nodes[it];
		int mid = (l + r) / 2;
		if (queryR <= mid)
			return query(queryL, queryR, l, mid, it * 2);
		else if (queryL >= mid)
			return query(queryL, queryR, mid, r, it * 2 + 1);
		else return query(queryL, mid, l, mid,
						  it * 2) +
					query(mid, queryR, mid, r,
						  it * 2 + 1);
	}
};
# PRESENTING CODE

已經找到了

取得中間值 區分左右節點範圍

完全在左

完全在右

左右都有

左右都有

注意範圍

變數解釋

nodesnodes 節點值

queryLqueryL queryRqueryR 搜尋左右界

ll rr 左右邊界

midmid 中介: 區分左右子區間

itit 偽指標: 指向結點在nodesnodes的位置

註: 皆為左閉右開 0-base

  • 他是好的
  • 一層最多碰到4的節點
  • 深度 : log(n)log(n)
  • 複雜度 : O(log(n))O(log(n))

複雜度

單點修改

Update

線段樹 單點修改 code

#include <bits/stdc++.h>
#define int long long
using namespace std;
struct SegmentTree {
	int n;
	vector<int> nodes;
	SegmentTree()
		: n(0) {
		nodes.clear();
	}
	SegmentTree(vector<int> &data)
		: n(data.size()) {
		nodes.resize(n * 4);
		build(data, 0, n);
	}
	void build(const vector<int> &data, int l, int r, int it = 1) {
		if (r - l == 1) {
			nodes[it] = data[l];
			return;
		}
		int mid = (l + r) / 2;
		build(data, l, mid, it * 2);
		build(data, mid, r, it * 2 + 1);
		nodes[it] = nodes[it * 2] + nodes[it * 2 + 1];
		return;
	}
	int query(int queryL, int queryR, int l, int r, int it = 1) {
		if (queryL == l && queryR == r) return nodes[it];
		int mid = (l + r) / 2;
		if (queryR <= mid)
			return query(queryL, queryR, l, mid, it * 2);
		else if (queryL >= mid)
			return query(queryL, queryR, mid, r, it * 2 + 1);
		else return query(queryL, mid, l, mid,
						  it * 2) +
					query(mid, queryR, mid, r,
						  it * 2 + 1);
	}
	void modify1(int pos, int modifyValue, int l, int r, int it = 1) {
		if (r - l == 1) {
			nodes[it] = modifyValue;
			return;
		}
		int mid = (l + r) / 2;
		if (pos < mid) modify1(pos, modifyValue, l, mid, it * 2);
		else modify1(pos, modifyValue, mid, r, it * 2 + 1);
		// pull
		nodes[it] = nodes[it * 2] + nodes[it * 2 + 1];
		return;
	}
};
# PRESENTING CODE

已經找到了 直接修改

取得中間值 區分左右節點範圍

在左

在右

修改目前節點值 (pull)

註: 皆為左閉右開 0-base

變數解釋

nodesnodes 節點值

queryLqueryL queryRqueryR 搜尋左右界

ll rr 左右邊界

midmid 中介: 區分左右子區間

itit 偽指標: 指向結點在nodesnodes的位置

  • 改值後 pull 上來
  • 複雜度就是最深深度
  • 複雜度 O(log(n))O(log(n))

複雜度

多點修改

Update

  • 懶人標記 (tag)
  • 兩種做法
    • 懶標下推 (推薦)
    • 懶標永久化

懶標

線段樹 懶標下推 code

#include <bits/stdc++.h>
#define int long long
using namespace std;
struct SegmentTree {
	int n;
	vector<int> nodes;
	vector<int> tags;
	SegmentTree()
		: n(0) {
		nodes.clear();
		tags.clear();
	}
	SegmentTree(vector<int> &data)
		: n(data.size()) {
		nodes.resize(n * 4);
		tags.resize(n * 4);
		build(data, 0, n);
	}
	void build(const vector<int> &data, int l, int r, int it = 1) {
		if (r - l == 1) {
			nodes[it] = data[l];
			return;
		}
		int mid = (l + r) / 2;
		build(data, l, mid, it * 2);
		build(data, mid, r, it * 2 + 1);
		nodes[it] = nodes[it * 2] + nodes[it * 2 + 1];
		return;
	}
	void push(int l, int r, int it) {
		int mid = (l + r) / 2;
		nodes[it * 2] += tags[it] * (mid - l);
		nodes[it * 2 + 1] += tags[it] * (r - mid);
		tags[it * 2] += tags[it];
		tags[it * 2 + 1] += tags[it];
		tags[it] = 0;
        return;
	}
	int query(int queryL, int queryR, int l, int r, int it = 1) {
		if (queryL == l && queryR == r) return nodes[it];
		push(l, r, it);
		int mid = (l + r) / 2;
		if (queryR <= mid)
			return query(queryL, queryR, l, mid, it * 2);
		else if (queryL >= mid)
			return query(queryL, queryR, mid, r, it * 2 + 1);
		else return query(queryL, mid, l, mid,
						  it * 2) +
					query(mid, queryR, mid, r,
						  it * 2 + 1);
	}
	void modify(int modifyL, int modifyR, int delta,
				int l, int r, int it = 1) {
		if (modifyL == l && modifyR == r) {
			nodes[it] += delta * (r - l);
			tags[it] += delta;
			return;
		}
		push(l, r, it);
		int mid = (l + r) / 2;
		if (modifyR <= mid)
			modify(modifyL, modifyR, delta, l, mid, it * 2);
		else if (modifyL >= mid)
			modify(modifyL, modifyR, delta, mid, r, it * 2 + 1);
		else {
			modify(modifyL, mid, delta, l, mid, it * 2);
			modify(mid, modifyR, delta, mid, r, it * 2 + 1);
		}
		// pull
		nodes[it] = nodes[it * 2] + nodes[it * 2 + 1];
		return;
	}
};
# PRESENTING CODE

新增宣告 tags (懶標)

build 函式 不需要改

註: 皆為左閉右開 0-base

變數解釋

nodesnodes 節點值

tagstags 懶標值

ll rr 左右邊界

midmid 中介: 區分左右子區間

itit 偽指標: 指向結點在nodesnodes的位置

線段樹 懶標下推 code push()

#include <bits/stdc++.h>
#define int long long
using namespace std;
struct SegmentTree {
	int n;
	vector<int> nodes;
	vector<int> tags;
	SegmentTree()
		: n(0) {
		nodes.clear();
		tags.clear();
	}
	SegmentTree(vector<int> &data)
		: n(data.size()) {
		nodes.resize(n * 4);
		tags.resize(n * 4);
		build(data, 0, n);
	}
	void build(const vector<int> &data, int l, int r, int it = 1) {
		if (r - l == 1) {
			nodes[it] = data[l];
			return;
		}
		int mid = (l + r) / 2;
		build(data, l, mid, it * 2);
		build(data, mid, r, it * 2 + 1);
		nodes[it] = nodes[it * 2] + nodes[it * 2 + 1];
		return;
	}
	void push(int l, int r, int it) {
		int mid = (l + r) / 2;
		nodes[it * 2] += tags[it] * (mid - l);
		nodes[it * 2 + 1] += tags[it] * (r - mid);
		tags[it * 2] += tags[it];
		tags[it * 2 + 1] += tags[it];
		tags[it] = 0;
        return;
	}
	int query(int queryL, int queryR, int l, int r, int it = 1) {
		if (queryL == l && queryR == r) return nodes[it];
		push(l, r, it);
		int mid = (l + r) / 2;
		if (queryR <= mid)
			return query(queryL, queryR, l, mid, it * 2);
		else if (queryL >= mid)
			return query(queryL, queryR, mid, r, it * 2 + 1);
		else return query(queryL, mid, l, mid,
						  it * 2) +
					query(mid, queryR, mid, r,
						  it * 2 + 1);
	}
	void modify(int modifyL, int modifyR, int delta,
				int l, int r, int it = 1) {
		if (modifyL == l && modifyR == r) {
			nodes[it] += delta * (r - l);
			tags[it] += delta;
			return;
		}
		push(l, r, it);
		int mid = (l + r) / 2;
		if (modifyR <= mid)
			modify(modifyL, modifyR, delta, l, mid, it * 2);
		else if (modifyL >= mid)
			modify(modifyL, modifyR, delta, mid, r, it * 2 + 1);
		else {
			modify(modifyL, mid, delta, l, mid, it * 2);
			modify(mid, modifyR, delta, mid, r, it * 2 + 1);
		}
		// pull
		nodes[it] = nodes[it * 2] + nodes[it * 2 + 1];
		return;
	}
};
# PRESENTING CODE

需要知道 子節點範圍

左右子節點都加上

懶標×範圍大小懶標 \times 範圍大小

更新子節點懶標

懶標歸0

push 完成

註: 皆為左閉右開 0-base

變數解釋

nodesnodes 節點值

tagstags 懶標值

ll rr 左右邊界

midmid 中介: 區分左右子區間

itit 偽指標: 指向結點在nodesnodes的位置

線段樹 懶標下推 code query()

#include <bits/stdc++.h>
#define int long long
using namespace std;
struct SegmentTree {
	int n;
	vector<int> nodes;
	vector<int> tags;
	SegmentTree()
		: n(0) {
		nodes.clear();
		tags.clear();
	}
	SegmentTree(vector<int> &data)
		: n(data.size()) {
		nodes.resize(n * 4);
		tags.resize(n * 4);
		build(data, 0, n);
	}
	void build(const vector<int> &data, int l, int r, int it = 1) {
		if (r - l == 1) {
			nodes[it] = data[l];
			return;
		}
		int mid = (l + r) / 2;
		build(data, l, mid, it * 2);
		build(data, mid, r, it * 2 + 1);
		nodes[it] = nodes[it * 2] + nodes[it * 2 + 1];
		return;
	}
	void push(int l, int r, int it) {
		int mid = (l + r) / 2;
		nodes[it * 2] += tags[it] * (mid - l);
		nodes[it * 2 + 1] += tags[it] * (r - mid);
		tags[it * 2] += tags[it];
		tags[it * 2 + 1] += tags[it];
		tags[it] = 0;
        return;
	}
	int query(int queryL, int queryR, int l, int r, int it = 1) {
		if (queryL == l && queryR == r) return nodes[it];
		push(l, r, it);
		int mid = (l + r) / 2;
		if (queryR <= mid)
			return query(queryL, queryR, l, mid, it * 2);
		else if (queryL >= mid)
			return query(queryL, queryR, mid, r, it * 2 + 1);
		else return query(queryL, mid, l, mid,
						  it * 2) +
					query(mid, queryR, mid, r,
						  it * 2 + 1);
	}
	void modify(int modifyL, int modifyR, int delta,
				int l, int r, int it = 1) {
		if (modifyL == l && modifyR == r) {
			nodes[it] += delta * (r - l);
			tags[it] += delta;
			return;
		}
		push(l, r, it);
		int mid = (l + r) / 2;
		if (modifyR <= mid)
			modify(modifyL, modifyR, delta, l, mid, it * 2);
		else if (modifyL >= mid)
			modify(modifyL, modifyR, delta, mid, r, it * 2 + 1);
		else {
			modify(modifyL, mid, delta, l, mid, it * 2);
			modify(mid, modifyR, delta, mid, r, it * 2 + 1);
		}
		// pull
		nodes[it] = nodes[it * 2] + nodes[it * 2 + 1];
		return;
	}
};
# PRESENTING CODE

每次到了就下推

註: 皆為左閉右開 0-base

變數解釋

nodesnodes 節點值

queryLqueryL queryRqueryR 搜尋左右界

ll rr 左右邊界

midmid 中介: 區分左右子區間

itit 偽指標: 指向結點在nodesnodes的位置

線段樹 懶標下推 code modify()

#include <bits/stdc++.h>
#define int long long
using namespace std;
struct SegmentTree {
	int n;
	vector<int> nodes;
	vector<int> tags;
	SegmentTree()
		: n(0) {
		nodes.clear();
		tags.clear();
	}
	SegmentTree(vector<int> &data)
		: n(data.size()) {
		nodes.resize(n * 4);
		tags.resize(n * 4);
		build(data, 0, n);
	}
	void build(const vector<int> &data, int l, int r, int it = 1) {
		if (r - l == 1) {
			nodes[it] = data[l];
			return;
		}
		int mid = (l + r) / 2;
		build(data, l, mid, it * 2);
		build(data, mid, r, it * 2 + 1);
		nodes[it] = nodes[it * 2] + nodes[it * 2 + 1];
		return;
	}
	void push(int l, int r, int it) {
		int mid = (l + r) / 2;
		nodes[it * 2] += tags[it] * (mid - l);
		nodes[it * 2 + 1] += tags[it] * (r - mid);
		tags[it * 2] += tags[it];
		tags[it * 2 + 1] += tags[it];
		tags[it] = 0;
        return;
	}
	int query(int queryL, int queryR, int l, int r, int it = 1) {
		if (queryL == l && queryR == r) return nodes[it];
		push(l, r, it);
		int mid = (l + r) / 2;
		if (queryR <= mid)
			return query(queryL, queryR, l, mid, it * 2);
		else if (queryL >= mid)
			return query(queryL, queryR, mid, r, it * 2 + 1);
		else return query(queryL, mid, l, mid,
						  it * 2) +
					query(mid, queryR, mid, r,
						  it * 2 + 1);
	}
	void modify(int modifyL, int modifyR, int delta,
				int l, int r, int it = 1) {
		if (modifyL == l && modifyR == r) {
			nodes[it] += delta * (r - l);
			tags[it] += delta;
			return;
		}
		push(l, r, it);
		int mid = (l + r) / 2;
		if (modifyR <= mid)
			modify(modifyL, modifyR, delta, l, mid, it * 2);
		else if (modifyL >= mid)
			modify(modifyL, modifyR, delta, mid, r, it * 2 + 1);
		else {
			modify(modifyL, mid, delta, l, mid, it * 2);
			modify(mid, modifyR, delta, mid, r, it * 2 + 1);
		}
		// pull
		nodes[it] = nodes[it * 2] + nodes[it * 2 + 1];
		return;
	}
};
# PRESENTING CODE

如果範圍正確

更新此節點值

加上懶標

有點像單點修改+區間查詢

下推

完全在左

完全在右

取得中間值

左右都有

修改目前節點值 (pull)

註: 皆為左閉右開 0-base

變數解釋

nodesnodes 節點值

tagstags 懶標值

modifyLmodifyL modifyRmodifyR 修改左右區間

deltadelta 修改增加值

複雜度

補充

迭代式線段樹

  • 節省一半記憶體
  • 常數小
  • O(n)O(n) 建樹 \leftrightarrow 遞迴式線段樹
  • 有點懸的存圖
  • code 短

優點

存圖方式 nn 為2的冪次

註: nn 代表原資料長度

迭代式線段樹 存圖 code

#include <bits/stdc++.h>
#define int long long
using namespace std;
struct SegmentTree {
	int n;
	vector<int> nodes;
	SegmentTree()
		: n(0) {
		nodes.clear();
	}
	SegmentTree(vector<int> &data)
		: n(data.size()) {
		nodes.resize(n << 1);
		build(data);
	}
	void build(const vector<int> &data) {
		for (int i = n; i--;) nodes[i + n] = data[i];
		for (int i = n; i--;)
			nodes[i] = nodes[i << 1] + nodes[i << 1 | 1];
		return;
	}
	void pull(int pos) {
		for (pos += n; pos >>= 1;)
			nodes[pos] = nodes[pos << 1] + nodes[pos << 1 | 1];
		return;
	}
	int query(int l, int r) {
		int sum = 0;
		for (l += n, r += n; l < r; l >>= 1, r >>= 1) {
			if (l & 1) sum += nodes[l++];
			if (r & 1) sum += nodes[--r];
		}
		return sum;
	}
	void modify1(int pos, int modifyValue) {
		nodes[pos + n] = modifyValue;
		pull(pos);
		return;
	}
};
# PRESENTING CODE

迭代式線段樹 build code

#include <bits/stdc++.h>
#define int long long
using namespace std;
struct SegmentTree {
	int n;
	vector<int> nodes;
	SegmentTree()
		: n(0) {
		nodes.clear();
	}
	SegmentTree(vector<int> &data)
		: n(data.size()) {
		nodes.resize(n << 1);
		build(data);
	}
	void build(const vector<int> &data) {
		for (int i = n; i--;) nodes[i + n] = data[i];
		for (int i = n; i--;)
			nodes[i] = nodes[i << 1] + nodes[i << 1 | 1];
		return;
	}
	void pull(int pos) {
		for (pos += n; pos >>= 1;)
			nodes[pos] = nodes[pos << 1] + nodes[pos << 1 | 1];
		return;
	}
	int query(int l, int r) {
		int sum = 0;
		for (l += n, r += n; l < r; l >>= 1, r >>= 1) {
			if (l & 1) sum += nodes[l++];
			if (r & 1) sum += nodes[--r];
		}
		return sum;
	}
	void modify1(int pos, int modifyValue) {
		nodes[pos + n] = modifyValue;
		pull(pos);
		return;
	}
};
# PRESENTING CODE

迭代式線段樹 query code

#include <bits/stdc++.h>
#define int long long
using namespace std;
struct SegmentTree {
	int n;
	vector<int> nodes;
	SegmentTree()
		: n(0) {
		nodes.clear();
	}
	SegmentTree(vector<int> &data)
		: n(data.size()) {
		nodes.resize(n << 1);
		build(data);
	}
	void build(const vector<int> &data) {
		for (int i = n; i--;) nodes[i + n] = data[i];
		for (int i = n; i--;)
			nodes[i] = nodes[i << 1] + nodes[i << 1 | 1];
		return;
	}
	void pull(int pos) {
		for (pos += n; pos >>= 1;)
			nodes[pos] = nodes[pos << 1] + nodes[pos << 1 | 1];
		return;
	}
	int query(int l, int r) {
		int sum = 0;
		for (l += n, r += n; l < r; l >>= 1, r >>= 1) {
			if (l & 1) sum += nodes[l++];
			if (r & 1) sum += nodes[--r];
		}
		return sum;
	}
	void modify1(int pos, int modifyValue) {
		nodes[pos + n] = modifyValue;
		pull(pos);
		return;
	}
};
# PRESENTING CODE

迭代式線段樹 單點修改 code

#include <bits/stdc++.h>
#define int long long
using namespace std;
struct SegmentTree {
	int n;
	vector<int> nodes;
	SegmentTree()
		: n(0) {
		nodes.clear();
	}
	SegmentTree(vector<int> &data)
		: n(data.size()) {
		nodes.resize(n << 1);
		build(data);
	}
	void build(const vector<int> &data) {
		for (int i = n; i--;) nodes[i + n] = data[i];
		for (int i = n; i--;)
			nodes[i] = nodes[i << 1] + nodes[i << 1 | 1];
		return;
	}
	void pull(int pos) {
		for (pos += n; pos >>= 1;)
			nodes[pos] = nodes[pos << 1] + nodes[pos << 1 | 1];
		return;
	}
	int query(int l, int r) {
		int sum = 0;
		for (l += n, r += n; l < r; l >>= 1, r >>= 1) {
			if (l & 1) sum += nodes[l++];
			if (r & 1) sum += nodes[--r];
		}
		return sum;
	}
	void modify1(int pos, int modifyValue) {
		nodes[pos + n] = modifyValue;
		pull(pos);
		return;
	}
};
# PRESENTING CODE
  • 一樣要懶標

多點修改

迭代式線段樹 code 懶標

#include <bits/stdc++.h>
#define int long long
using namespace std;
struct SegmentTree {
	int n;
	vector<int> nodes;
	vector<int> tags;
	SegmentTree()
		: n(0) {
		nodes.clear();
		tags.clear();
	}
	SegmentTree(vector<int> &data)
		: n(data.size()) {
		nodes.resize(n << 1, 0);
		tags.resize(n, 0);
		build(data);
	}
	void build(const vector<int> &data) {
		for (int i = n; i--;) nodes[i + n] = data[i];
		for (int i = n; i--;)
			nodes[i] = nodes[i << 1] + nodes[i << 1 | 1];
		return;
	}
	void pull(int pos) {
		pos += n;
		for (int h = 1; pos >>= 1; h++)
			nodes[pos] = nodes[pos << 1] + nodes[pos << 1 | 1] +
						 (tags[pos] << h);
		return;
	}
	void push(int pos) {
		pos += n;
		for (int h = __lg(pos), it = 1; h--; it = pos >> h) {
			if (!tags[it]) continue;
			nodes[it << 1] += tags[it] << h;
			nodes[it << 1 | 1] += tags[it] << h;
			if (h) {
				tags[it << 1] += tags[it];
				tags[it << 1 | 1] += tags[it];
			}
			tags[it] = 0;
		}
	}
	int query(int l, int r) {
		push(l), push(r - 1);
		int sum = 0;
		for (l += n, r += n; l < r; l >>= 1, r >>= 1) {
			if (l & 1) sum += nodes[l++];
			if (r & 1) sum += nodes[--r];
		}
		return sum;
	}
	void modify(int l, int r, int delta) {
		push(l), push(r - 1);
		int l2 = l + n, r2 = r + n;
		if (l2 & 1) nodes[l2++] += delta;
		if (r2 & 1) nodes[--r2] += delta;
		for (int h = 1; (l2 >>= 1) < (r2 >>= 1); h++) {
			if (l2 & 1) tags[l2] += delta, nodes[l2++] += delta << h;
			if (r2 & 1) tags[--r2] += delta, nodes[r2] += delta << h;
		}
		pull(l), pull(r - 1);
	}
};
signed main() {
	int n, q;
	cin >> n >> q;
	vector<int> data(n);
	for (int &i : data) cin >> i;
	SegmentTree st(data);
	for (int mode, l, r, delta; q--;) {
		cin >> mode;
		if (mode == 1) {
			cin >> l >> r >> delta;
			st.modify(l - 1, r, delta);
		}
		else {
			cin >> l;
			cout << st.query(l - 1, l) << "\n";
		}
	}
}
# PRESENTING CODE

指標型線段樹

指標型線段樹

#include <bits/stdc++.h>
#define int long long
using namespace std;
struct node {
	int l, r;
	int value = 0, tag = 0;
	node *pointerL = nullptr, *pointerR = nullptr;
	node(int _l, int _r)
		: l(_l), r(_r) {}
	node(vector<int> &data) {
		l = 0;
		r = data.size();
		build(data);
	}
	void build(const vector<int> &data) {
		if (r - l == 1) {
			value = data[l];
			return;
		}
		int mid = l + r >> 1;
		pointerL = new node(l, mid);
		pointerR = new node(mid, r);
		pointerL->build(data);
		pointerR->build(data);
		value = pointerL->value + pointerR->value;
		return;
	}
	void push() {
		if (r - l == 1) return;
		pointerL->value += tag * (pointerL->r - l);
		pointerR->value += tag * (r - pointerR->l);
		pointerL->tag += tag;
		pointerR->tag += tag;
		tag = 0;
	}
	int query(int queryL, int queryR) {
		if (queryL == l && queryR == r) return value;
		push();
		if (queryR <= pointerL->r)
			return pointerL->query(queryL, queryR);
		else if (queryL >= pointerR->l)
			return pointerR->query(queryL, queryR);
		else return pointerL->query(queryL, pointerL->r) +
					pointerR->query(pointerR->l, queryR);
	}
	void modify(int modifyL, int modifyR, int delta) {
		if (modifyL == l && modifyR == r) {
			value += delta * (r - l);
			tag += delta;
			return;
		}
		push();
		if (modifyR <= pointerL->r)
			pointerL->modify(modifyL, modifyR, delta);
		else if (modifyL >= pointerR->l)
			pointerR->modify(modifyL, modifyR, delta);
		else {
			pointerL->modify(modifyL, pointerL->r, delta);
			pointerR->modify(pointerR->l, modifyR, delta);
		}
		// pull
		value = pointerL->value + pointerR->value;
		return;
	}
};
signed main() {
	int n, q;
	cin >> n >> q;
	vector<int> data(n);
	for (int &i : data) cin >> i;
	node st(data);
	for (int mode, l, r, u, k; q--;) {
		cin >> mode;
		if (mode == 1) {
			cin >> l >> r >> u;
			st.modify(l - 1, r, u);
		}
		else {
			cin >> k;
			cout << st.query(k - 1, k) << "\n";
		}
	}
}
# PRESENTING CODE

平衡二元樹

  • 二元

二元樹

🌲

樹 結構

根節點

葉節點

分支

根節點的

子節點

  • 節點 + 分支
  • 根節點 : 節點根源
  • 葉節點 : 無子節點
  • 子節點 : 向下連結到的
  • 深度 : 根到此節點的路徑長

樹結構

樹 結構

根節點

葉節點

分支

根節點的

子節點

  • 每個節點至多 2 個子節點

二元樹

二元?

至多兩個子節點

二元樹 結構

根節點

葉節點

根節點的

子節點

分支

Made with Slides.com