---
# System prepended metadata

title: segment tree
tags: [109_FDCS]

---

---
tags: 109_FDCS
---

# segment tree

線段樹是一種二元樹形資料結構
用以儲存區間或線段，並且允許快速查詢結構內包含某一點的所有區間
一個包含 $n$ 個區間的線段樹，空間複雜度為 $O(n)$ ，查詢的時間複雜度則為 $O(logn+k)$ ，其中 $k$ 是符合條件的區間數量

:::spoiler 時間複雜度 💩
![](https://i.imgur.com/yBiu5cO.png)
:::

---

## 線段樹示意圖

![](https://i.imgur.com/n79odXF.png)

---

## 基本操作

### 單點修改 & 區間查詢

#### 單點修改

從根結點開始找
分成三種情況討論

##### 1. 完全不包含
不用做任何操作，直接return

##### 2. 完全包含
代表當前節點只有一個元素
直接修改即可

##### 3. 部分包含
遞迴左右子樹直到符合上面兩個任一


#### 區間查詢
一樣分三種情況

##### 1. 完全不包含
return 0

##### 2. 完全包含
return 當前節點的值

##### 3. 部分包含
遞迴左右子樹直到符合上面兩個任一

**可以發現兩種操作基本上 完 全 一 致**

---

### 區間修改 & 單點查詢

教BIT的時候再講 :poop:

---


## 常見的實作方法

### 指標

##### 優點
- 直觀
- 可持久化

##### 缺點
- 記憶體肥
- 指標比較慢


#### code

```cpp=
struct node {
	ll val;
	node *l, *r;
	node* pull() {val = (l ? l->val : 0) + (r ? r->val : 0); return this;}
	node(ll x): val(x), l(nullptr), r(nullptr) {}
	node(node* a, node* b): l(a), r(b) {pull();}
};

using stree = node*;

ll n, arr[1000005];

#define m ((l + r) >> 1)

stree build(int l = 0, int r = n) {
	if (r - l == 1) return new node(arr[l]);
	else return new node(build(l, m), build(m, r));
}

stree modify(stree pre, int p, ll k, int l = 0, int r = n) {
	if (p < l || r <= p) return pre;
	if (r - l == 1) {pre->val = k; return pre;}
	modify(pre->l, p, k, l, m), modify(pre->r, p, k, m, r);
	return pre->pull();
}

ll query(stree cur, int ql, int qr, int l = 0, int r = n) {
	if (qr <= l || r <= ql) return 0;
	if (ql <= l && r <= qr) return cur->val;
	return query(cur->l, ql, qr, l, m) + query(cur->r, ql, qr, m, r);
}

void print(stree cur, int l = 0, int r = n) {
	if (r - l == 1) {cout << cur->val << ' '; return;}
	print(cur->l, l, m), print(cur->r, m, r);
}

#undef m

inline void solve() {
	string str;
	getline(cin, str);
	stringstream ss(str);
	while (ss >> arr[n]) n++;
	int q, l, r; cin >> q;
	stree seg = build();
	while (q--) {
		cin >> l >> r;
		if (l > r) swap(l, r);
		cout << query(seg, l - 1, r) << endl;
	}
}
```

---

### 陣列

##### 優點
- 比較不占空間
- 下標存取比指標快一些

##### 缺點
- 要用到一些位元運算
- 比較不直觀

#### code

```cpp=
constexpr int maxn = 200000;
ll n = 0, seg[maxn << 2], arr[maxn + 5];

#define m ((l + r) >> 1)

void build(int i = 1, int l = 0, int r = n) {
	if (r - l == 1) {seg[i] = arr[l]; return;}
	build(i << 1, l, m), build(i << 1 | 1, m, r);
	seg[i] = seg[i << 1] + seg[i << 1 | 1];
}

void modify(int p, ll k, int i = 1, int l = 0, int r = n) {
	if (p < l || r <= p) return;
	if (r - l == 1) {seg[i] = k; return;}
	modify(p, k, i << 1, l, m), modify(p, k, i << 1 | 1, m, r);
	seg[i] = seg[i << 1] + seg[i << 1 | 1];
}

ll query(int ql, int qr, int i = 1, int l = 0, int r = n) {
	if (qr <= l || r <= ql) return 0;
	if (ql <= l && r <= qr) return seg[i];
	return query(ql, qr, i << 1, l, m) + query(ql, qr, i << 1 | 1, m, r);
}

void print(int i = 1, int l = 0, int r = n) {
	if (r - l == 1) {cout << seg[i] << ' '; return;}
	print(i << 1, l, m), print(i << 1 | 1, m, r);
}

#undef m

inline void solve() {
	string str;
	getline(cin, str);
	stringstream ss(str);
	while (ss >> arr[n]) n++;
	build();
	int q, l, r; cin >> q;
	while (q--) {
		cin >> l >> r;
		if (l > r) swap(l, r);
		cout << query(l - 1, r) << endl;
	}
}
```

