---
# System prepended metadata

title: NYCU PCCA 2022 Winter Camp 0125

---

# NYCU PCCA 2022 Winter Camp 0125
## [Part 1. STL](https://www.youtube.com/watch?v=Pp4tgANoWC4&list=PLtRlodjJ-YdIizrpGnftvH7hgJEQN7j9E&index=1)
### I. vector
#### 1. 常用函式
``` cpp=
#include <vector>
vector<int> v;
 
v.size(); // v 的長度
v.resize(n); // 調整大小為 n
v.clear(); // 初始化 v
v.push_back(val); // 放 val 在 v 後面
v.begin(); v.end(); // v 的 iterator
```
#### 2. vector vs. array
|vector|array|
|------|-----|
|可更動大小|不能更動大小|
|宣告較複雜|宣告簡單|
|功能多|很單純的陣列|

#### 3. 其他函式說明
* `vector<int> v(n, 0);`
>int: 陣列中要放的型別
v: 名字
n: 長度
0: 初始值(沒放的話則會是預設值)

二維陣列 
```cpp
vector<vector<int>> v(n,vector<int> (n, 0));
```
* `v.push_back(val);`
把 val 的值複製
* `v.emplace_back(val);`
把 val 做為參數來宣告
>兩者速度基本沒差 除非加入的東西很大

>**push_back() v.s. emplace_back()**
emplace_back() 的好處在於它可以減少「宣告」的繁瑣(如下)
``` cpp=
vector<pair<int, int>> arr;
int main() {
    arr.push_back(make_pair(2, 5)); 
        // 要先產生 pair 才可以被複製
    arr.emplace_back(2, 5);
        // 簡潔許多
    return 0;
}
```

* `v.resize(n)`
把 v 長度設為 n

* `v.resize(n, val)`
如果 v 需要增加長度，值會是 val
已存在的值不會被改變！！！
* `v.assign(n, val)`
把 v 初始化成長度為 n 且值為 val
* `v.reserve(n)`
在陣列後面預留 n 的空間
>v 的 size 仍不變
>
>用來避免 push_back() 時，可能需要花 O(n) 的時間搬移 v 到連續記憶體更長的地方

* **請不要用 erase(), insert()**
vector 是連續儲存的，所以移除或插入在中間會需要 O(n) 的時間
**" 這兩個東西是毒瘤啊！ "**


---

所以現在是 iterator 時間
> " iterator 迭代器，簡單來說就是指標。 "
* `v.begin()`: 回傳指向第 0 個的 iterator
* `v.end()`: 回傳指向最後一項的下一項的 iterator
所以 v.begin() 到 v.end() 是左閉右開

* `v.rbegin()`: 回傳指向最後一項的 iterator
* `v.rend()`: 回傳指向第 0 項的前一項的 iterator

``` cpp=
#include <vector>
vector<int> v(10);
 
int main() {
    for (int &i: v) cin >> i; // 輸入 10 個東西到 v
    
    for (auto it = v.begin(); it != v.end(); ++it) {
        cout << *it << '\n'; // 記得加 * 在變數前面來取值
    }
}
```

### II. tuple
適合==儲存一組資料的東西==（例如： x, y 座標）
```cpp=
#include <tuple>
#include <string>
#include <vector>
tuple<int, long long, string> t = {13, 53, "hello"};
pair<int, long long> p = {11, 42};
pair<int, vector<int> > pp; // 要存 vector 也可以
 
get<0>(t) // 回傳 t 的第 0 個東西 (13)
p.first; p.second; // 分別回傳 p 的第 0 項跟第 1 項
```
tuple 最多可以存 10 樣東西，但是較難使用
而 pair 是特化版的 tuple ，只能存兩種東西
宣告方式有以下兩種：
```cpp
pair<int, long long> p = {11, 42};
make_pair(11, 42);
```
> 小技巧
> 使用 #define 讓 first, second 更短
```cpp
#define first f
#define second s
```

### III. algorithm
```cpp
#include <algorithm> // 我都用萬用標頭檔哈哈 
```
* **sort()**
" 你只要知道它的時間複雜度是 O(n log n)，很快就對了。 "
`sort(v.begin(), v.end());`
把 v.begin() 到 v.end() 之間排序
`sort(v.begin(), v.end(), cmp);`
依照自訂的 cmp 函式來排序
```cpp=
#include <algorithm>
#include <vector>
vector<int> v = {1, 3, 5, 2, 4};
bool cmp(const int &x, const int &y) {
    return x > y;
}
int main() {
    sort(v.begin(), v.end()); // 由小到大（預設）
    sort(v.begin(), v.end(), cmp); // 由大到小（利用 cmp 函式）
    sort(v.rbegin(), v.rend()); // 也是由大到小（倒著排序）
}
```
* **lower_bound(), upper_bound()**
配合二分搜尋，時間複雜度 O(log n)，特別注意==vector要先排好！==
`lower_bound(v.begin(), v.end(), val);`
在範圍找出 ≥ val 中，最小的 iterator
`upper_bound(v.begin(), v.end(), val);`
在範圍找出 > val 中，最小的 iterator
* **nth_element()**
`nth_element(v.begin(), v.begin() + val, v.end());`
時間複雜度 O(n)，類似快速排序(?)
在範圍中，找出第 val 項
第 val 項之前的值都會 ≤ 第 val 項
第 val 項之後的值都會 ≥ 第 val 項
可以放 cmp
* **next_permutation()**
`next_permutation(v.begin(), v.end());`
時間複雜度 O(n / 2)
可以找出下一字典序大的排列 (prev_permutation() 可以找下一小的)
也可以放 cmp
{1, 2, 3, 4, 5} -> {1, 2, 3, 5, 4}

### IV. set
沒錯，就是數學課教的集合
#### 1. 常用函式
```cpp=
#include <set>
set<int> s;
 
s.insert(val); // 在 s 中放入 val
s.erase(val); // 把 val 從 s 中移除
s.clear(); // 清空 s
s.size(); // s 的大小
s.find(val); // 找 val 的 iterator
s.count(val); // 找 val 在 s 中的數量
s.begin(); s.end(); // iterator
```
#### 2. 特色
* **值不能重複** （multiset 支援插入重複的值）
* **會自動排序**
可以快速取最大、最小值
(unordered_set 則不會排序)
* **不能隨機取值**
就是不行用 s[2] 來找第 2 小的東西啦
* **可以用 iterator 迭代**

#### 3. 說明
* `set<int> s;`
* `set<int, cmp> sc;`
依照 cmp 去排序
但是跟普通宣告 cmp 的方式不太一樣…
```cpp=
#include <set>
 
struct cmp {
    bool operator() (const int &a, const int &b) const {
        return a > b;
    }
};
 
set<int> s; // 由小到大排列
set<int, cmp> sc; // 由大到小排列
```
* `s.insert(val);`
放 val 進 s 裡面
* `s.emplace(val);`
和 vector 一樣
* `s.erase(val);`
在 set 中移除值為 val 的東西
回傳被移除後的個數
val 也可以是 iterator
回傳被移除後的下一個 iterator（下一個比它大的）
:::info
erase() 的毒瘤操作
```cpp=
#include <set>
set<int> s;
 
int main() {
    s.emplace(1); s.emplace(2); s.emplace(3);
    for (auto it = s.begin(); it != s.end(); ) {
        if (*it == 2) it = s.erase(it); 
            // 如果 it 指向 2，erase() 以後會回傳 3 的 iterator
        else it++;
    }
}
```
:::
* `s.find(val);`
回傳 val 的 iterator
如果 ==val 不存在，則會回傳 s.end()==
* `s.count(val);`
回傳 val 的個數
set 同樣的值只能有一個，所以==只會回傳 0 或 1==


---

所以現在是 iterator 時間
* s.begin() 指向第 0 項（最小值）
* s.rbegin() 指向最後一項（最大值）

跟 vector 一樣，可以用 for 迴圈迭代

### V. map
" 跟字典一樣，問一個答一個。 "
#### 1. 常用函式
```cpp=
#include <map>
map<int, string> m; // <key, value>
 
m[1] = "NYCU"; m[5] = "PCCA"; // 定義
m.clear(); // 清空
m.find(val); // 回傳 key = val 的 iterator
m.count(val); // 回傳 key = val 的個數
m.begin(); m.end(); // iterator
```
#### 2. 特色
* **每個元素一個 pair** ({key, value})
* **key 不能重複** (multimap 支援 key 重複)
* **會自動排序** (依據 key 進行排序(小 → 大))
unordered_map 則不會排序
```cpp=
#include <map>
map<int, int> m;
 
m[2] = 7; // 定義
m[4]++; // 如果索取的 key 未被定義，則會使用預設(0)
        // m[4] = 1
```
#### 3. 其他函式
* `m.find();`
例如： m.find(2)
尋找 key = 2 的元素
回傳 pair {2, 7}
* **iterator**
```cpp=
#include <map>
map<int, int> m;

for (pair<const int, int> &i: m) {
    cout << "key: " << i.first << ' ';
    cout << "value: " << i.second << '\n';
}
```

### VI. priority queue
#### 1. 常用函式
```cpp=
#include <queue> // 它在queue裡面欸呵呵
priority_queue<int> pq;
 
pq.empty(); // 是否空了
pq.size(); // 大小
pq.top(); // pq 中，值最大的
pq.pop(); // pq 中，移除值最大的
pq.push(val); // 把 val 放入 pq 中
```
:::warning
**跟 queue 的不同**
queue 是 FIFO ( first in first out )
priority queue 不是用時間作為排序依據，而是以大小排序
:::
#### 2. 特色
* **預設是<font color="#f00">從大到小</font>排列！！！**(可以使用cmp)
`priority_queue<T, vector<T>, less<T>>`
T 表示資料型態
less<T> 表示資料由大到小排(預設)
改為使用 greater<T> 就會是由大到小排
你也可以寫 cmp
```cpp=
struct cmp { // 由小到大
    bool operator() (int &a, int &b) {
        return a > b; // 跟前面的反過來，有點毒瘤，要小心
    }
};
```
    
### VII. stringstream
#### 1. 常用函式
```cpp=
#include <sstream>
stringstream ss;
int a, b;
 
ss << "12 53"; // ss 寫入 "12 53"
ss >> a >> b; // 從 ss 讀出，a = 12, b = 53
ss.clear(); // 清除 flag
ss.str("PCCA"); // 把 ss 的內容設定為 "PCCA"
```
stringstream 宣告速度不快，所以==請避免重複宣告==，多資源回收～
* **寫入讀出**
寫入： ss << "Hello";
讀出： ss >> s;
其實就跟 cin 、 cout 一樣啦
* **`ss.clear()`**
它只會初始化 ss 裡的 flag ，裡面的 buffer 不會清空！<font size = 5>不會清空！</font><font size = 6>不會清空！</font>
那我們要怎麼把它清空呢？ 請使用下方的工具 str()
* **`ss.str()`**
`ss.str("PCCA");`
把 ss 的內容設成 “PCCA”
`ss.str();`
回傳目前 ss 裡的字串
:::success
**清空 stringstream 的方式**
``` cpp
ss.str("") // 把它變成空字串
ss.clear() // 把一些不用的東西(例：EOF)清掉
```
:::


---

## [Part 2. DSU](https://https://www.youtube.com/watch?v=8dsEw2ndqCI&list=PLtRlodjJ-YdIizrpGnftvH7hgJEQN7j9E&index=2)
DSU = Disjoin-Set Union
    有些集合，他們<font color="#f00">**互相交集為空集合**，</font>則這些集合稱為並查集，又稱互斥集。
### 舉個例子：
學校裡有一些幫派，每個人只會屬於一個幫派，若某人==不加入別人，代表他自己就是一個單人幫派。==

幫派之間會互相鬥爭，==贏得一方可以把對方所有人納入自己的幫派==，也就是把兩個幫派合併成一個。

剛剛說的幫派是一群人組成的，我們只知道他是一個群體，但要怎麼表示這個群體最簡單呢？

我們可以從這個幫派之中，隨便挑一個人來當老大，==老大的作用就是用來代表這群體。==
    
我們可以透過不斷詢問老大的老大的老大的老大. . . 直到有人的老大是自己，就可以確定這個人是群體的代表。

![](https://i.imgur.com/Md8NSd0.png)
▲ 框在一起的是同個幫派的成員；紅色底的是該幫派的老大。

#### 但這樣會比較快嗎？ 最差狀況還是 O(n) 查詢
* parent[n] (boss[n])
用來表示你的上層是誰，用 parent[i] == i 來偵測自己是否是最上層。

* size[n]
用來紀錄集合的大小，只有在 index 是最終老大時才有意義。
    
* 精簡版陣列 pa_sz[n]
``` cpp
if (parent[i] != i) {
    pa_sz[i] = parent[i];
} else {
    pa_sz[i] = -size[i];
}     
```
* find()
find() 用來尋找最終老大。剛剛說過，一個一個找，最差情況會花費 O(n) 的時間。
``` cpp
int find(int index) {
    if(parent[index] == index) return index;
    return find(parent[index]); // 一路往上找
}
```
![](https://i.imgur.com/uSLXc08.png)
▲ 假設今天1的幫派和5的幫派打架，5的幫派獲勝，那1的幫派就會被5的幫派併吞。
![](https://i.imgur.com/Fmil7Z1.png)
▲ 而我們就把1的老大設定成5，5的幫派因此人數增加。1也因為不是最終老大，所以size[1]沒有用了。

* find 路徑壓縮
路徑壓縮就是在找老大的過程中
順便把路徑上所有點的老大也設成最終老大
``` cpp
int find(int index) {
    if(parent[index] == index) return index;
    return parent[index] = find(parent[index]); // 路徑壓縮
}
```
    
* union
合併兩個集合a, b
``` cpp
void Union(int a, int b) {
    int fa = find(a), fb = find(b);
    size[fa] += size[fb];
    parent[fb] = fa;
}
```

* 啟發式合併
把小的集合接在大的集合下面，之後find會更快
``` cpp
void Union(int a, int b) {
    int fa = find(a), fb = find(b);
    if(size[fb] > size[fa]) swap(fa, fb);
    size[fa] += size[fb];
    parent[fb] = fa;
}
```

* 時間複雜度
都沒有： O(n)
路徑壓縮： O(log n)
啟發式合併： O(log n)
啟發式合併＋路徑壓縮： O(α(n)) (α = 反阿卡曼函數)

* 課後練習
[Kattis - Ladice](https://open.kattis.com/problems/ladice)
[Kattis - Association for Control Over Minds](https://open.kattis.com/problems/control)
[Codeforces - Social Network](https://codeforces.com/contest/1609/problem/D)

---

## [Part 3. Heap](https://www.youtube.com/watch?v=cdakZQa2pXE&list=PLtRlodjJ-YdIizrpGnftvH7hgJEQN7j9E&index=3)
### 1. 資料型態
* min heap (最小堆積)：母節點的值恆小於等於子節點
* max heap (最大堆積)：母節點的值恆大於等於子節點
### 2. 操作 (以 min heap 為例)
* push()
將元素加入 heap 中
* top()
從 heap 中取出最小元素
* pop()
將 heap 中最小的元素去除

還有很多，只是這些夠玩了
### 3. 如何實現
把資料擺成 Complete Binary Tree
保證子樹一定不小於自己，但左右子樹大小不保證
示意圖：
![](https://i.imgur.com/MIZN7Da.jpg)
### 4. 樹的儲存方法
因為是 binary tree，所以可用array存取。
根節點 idx=0
對於所有節點，若節點編號為 i，則左邊的孩子編號為 2i+1，且右邊的孩子編號為 2i+2
### 5. how 函式 work？
* **top**
獲得最小值(根節點)
* **push**
把要加入的值放入heap最後面，再慢慢跟上面的值比大小
例如：把0加入heap中
![](https://i.imgur.com/MIZN7Da.jpg)
▼ 先把0放到最後面(vector的push_back())
![](https://i.imgur.com/JMWYWUT.jpg)
▼ 跟上面的值比大小，比較小就往上移，像泡泡浮起來一樣
![](https://i.imgur.com/fUUKC0B.jpg)
![](https://i.imgur.com/MKI6hVu.jpg)
▼ 比到最後，就會發先0是最小的，所以位置在根節點
![](https://i.imgur.com/NhjQRLk.jpg)
* **pop**
如果直接把最小值(根節點)刪掉，heap就會分裂成2個，下次要找最校值就會比較難處理。所以先把最小值和尾端交換，再把最後一個元素刪除(pop_back)，然後將現在的根節點和下面兩個值比，並且跟最小值交換
例如：把0刪掉
![](https://i.imgur.com/wqST8lV.jpg)
▼ 先把0和最後一個元素交換
![](https://i.imgur.com/fewUViD.jpg)
▼ 刪除最後一個元素
![](https://i.imgur.com/OJKoYTg.jpg)
▼ 讓19和下面兩個值比大小，並跟最小值(1)交換
![](https://i.imgur.com/F8czb8H.jpg)
![](https://i.imgur.com/zO4nO0J.jpg)
▼ 繼續比直到19為3數值中最大或到達根部
![](https://i.imgur.com/pH5woi1.jpg)

### 6. 時間複雜度分析
* top： O(1)
* push, pop： O(log n)

### 7. 課後練習
priority queue
[Kattis - Jane Eyre](https://open.kattis.com/problems/janeeyre)
[Kattis - Continuous Median](https://open.kattis.com/problems/continuousmedian)
    
## [Part 4. BIT (Binary Index Tree)](https://www.youtube.com/watch?v=bCsU5hlc5jI&list=PLtRlodjJ-YdIizrpGnftvH7hgJEQN7j9E&index=4)
用來處理動態區間和
BIT 會把原本的陣列
用很多長度為二的冪次的小塊區間表示，
在計算或是修改時，都去對那些小區間修改。
使用 BIT 會將時間複雜度變為 O(log n)

* bit[maxn] : 長度為整個區間 [1, n] 的一維陣列。
上面每個點儲存的不是該點真正的值，而是從這個節點往前**二的冪次**長度的區間和。
注意這是 1-base 的陣列。
![](https://i.imgur.com/lLewWkt.png)
    
* lowbit：我們定義最右邊出現的第一個 1 為 lowbit。
    e.g. 10010 的 lowbit 為右邊第二個 bit 的 1。
    一個點編號 13=8+4+1=1101 (2-base)
可以由 [1,8]+[9,12]+[13,13] 加總起來疊加出正確的 [1,13] 區間。
至於為什麼是這些區間，跟 lowbit 有很大關係。
    
#### 要如何正確且快速地找出那些小區間塊呢？
不斷找出當前 index 的 lowbit 將其扣掉，得出新的 index。

在查詢[1, i]的區間和時，先查格子 i (1101)，
再把lowbit拿掉，如此重複下去：
``` cpp
i=1101 -> sum+=bit[1101], i-=0001
i=1100 -> sum+=bit[1100], i-=0100
i=1000 -> sum+=bit[1000], i-=1000
i=0000 -> break
```

#### 只花 O(1) 找出 lowbit 的方法：
``` cpp
lowbit = i & -i;
```

舉例：
    i = 10100, -i = 01100 (二補數)
    i & -i = 00100
    
原理：
    i 與 -i 加起來要讓它變成全部位數都是0 (溢位不算)
    然後取 i 與 -i 位數都是 1 的就是 lowbit

> **二補數**
一個數的二補數是從它二進位表示的最左方開始，找到第一個 1 之後，右邊的 0, 1 互換

* query (詢問)
在詢問某區間和時
只要用剛剛的方法看他被哪些小區塊覆蓋
把他們加起來就是該區間和。
``` cpp
ll sum(int i) {
    ll ret = 0;
    while(i > 0) ret += bit[i], i -= i & -i; // 1-base
    return ret;
}
```
要查詢閉區間 [l, r] 時，只要找出 [1, r]，扣掉 [1, l - 1]，就是[l, r] 的區間和了。

#### 但要修改怎麼辦？
當我們改了 index 這個點，要怎麼知道上面有哪些點有包含 bit[index] 所代表的區間呢？

* 單點修改
假設要修改 5 = 101 (2-base)
先改 i 的值，再把 i 加上 lowbit，反覆改 i，加值。
``` cpp
i = 101 -> bit[101]+=val, i = 110
i = 110 -> bit[110]+=val, i = 1000
i = 1000 -> bit[1000]+=val, i = 10000
i = 10000 -> bit[10000]+=val, i = 100000
// ...直到 i 超過 maxn -> break
```

* 區間修改
可以用線段樹做(詳情見Part 5. Segment Tree)

* 先找出某個單點 i，被哪些子區間重疊到
要把這個單點的值增加 val
就把跟他有關的所有區間都加上 val
``` cpp
void modify(int i, int val) {
    while(i <= MAXN) bit[i] += val, i += i & -i;
}
```

#### 課後練習
[TIOJ - 1080](https://tioj.ck.tp.edu.tw/problems/1080)
[Codeforces round 196 - C](https://codeforces.com/contest/276/problem/C)
                   
---

## [Part 5. Segment Tree](https://www.youtube.com/watch?v=Hfue-_pVFPc&list=PLtRlodjJ-YdIizrpGnftvH7hgJEQN7j9E&index=5)
    
前面 BIT 有提到，做區間和的時候如果每次都從頭計算，詢問的話要花 O(n) 的時間，實在是有點太久了。你其實可以動一下腦，你就會發現，你可以預處理好前綴和，前綴和做完可以大幅加速詢問的效率。但缺點是，你每修改一個值，你就要花 O(n) 時間改你做好前綴和的表。
    
於是線段樹這個東西就出現了，它是個很好玩的資料結構。
### 1. 功能介紹
<font color = "#f00" size = 4>每次操作都是 O(log n)！！！</font>
* 單點修改
* 區間查詢
* 區間修改
    
線段樹有另一個好處是，它不只可以求區間和，它可以做任何有結合律的二元運算子的操作，像是找最小值、最大值，或是乘法。
    
當你要訪問一個區間的時候，其實不用按照你擺的順序從頭到尾做。

### 2. 基本概念
* 善用運算子具有結合率的性質
* 每次詢問時，將大問題切成子問題
* 事先預處理一些區間的答案
* 更新時只需要更新與之相關的區間

### 3. 實現方法：建樹
* 所有節點都有各自代表的範圍的答案
* 每個節點都可以利用子樹資訊得到
* 葉節點存初始陣列數值
* 用陣列存最多只有 4n 個節點
    
▼ 例如： arr = [-9, 11, 1, 3, -7, -4, 10, -5]
![](https://i.imgur.com/bxDzKmT.jpg)
    
它的存法跟 Heap 一樣，因為它也是一棵二元樹，所以你可以把根節點的 index 設成 0，它的 left child 和 right child 就分別是 2n+1 和 2n+2。
    
### 區間查詢
在 [l, r) 區間內詢問 [L, R) 時只會有以下三種狀況：
* 1. [L, R) = [l, r)
* 2. [L, R) ⊊ [l, r)
    2-1. 目標區間在左半或右半 ( L ≥ mid ∨ R ≤ mid )
    2-2. 目標區間橫跨中點

**狀況一**：目標區間與當前區間相等
直接回傳當前區間
    
**狀況二**：目標區間與當前區間不相等（都在左半或右半）
縮小問題，進入左（右）子區間

**狀況三**：目標區間與當前區間不相等（橫跨中點）
縮小問題，分別進入左、右子區間詢問
回傳子區間合併後的答案
    
例：求 [2, 4) + [4, 7) 的區間和
![](https://i.imgur.com/7Zw3FrF.jpg)
![](https://i.imgur.com/ptT7NAy.jpg)
▼ 會變成求 [2, 4) + [4, 6) + [6, 7) 的區間和
![](https://i.imgur.com/ef1mcS2.jpg)
![](https://i.imgur.com/o7eguGO.jpg)
最後答案是 4 + (-11) + 10 = 3


### 單點修改
利用類似二分搜的想法找到目標
回溯時依序更改與之相關的節點資訊

例：a[2] = 7
![](https://i.imgur.com/4k5hq9e.jpg)
![](https://i.imgur.com/PY993Nx.jpg)
![](https://i.imgur.com/5KpstX3.jpg)
![](https://i.imgur.com/24sdOIQ.jpg)
![](https://i.imgur.com/6OVP7sU.jpg)
![](https://i.imgur.com/X2qmIVV.jpg)
![](https://i.imgur.com/DzyMJiF.jpg)
![](https://i.imgur.com/Bf4ILRW.jpg)
    
### 4. 使用時機
* max(),min(),gcd(),lcm()
* +, *, 矩陣乘法
* 線段樹上二分搜
二分搜 query 出來的答案需要 O(log^2^ n)
但直接在線段樹上二分搜只需要 O(log n)
A. 0 的個數 (搜尋第 k 個 0 在哪裡)
B. 給定前綴和
C. lower_bound

### 5. 進階技巧
* lazy tag (懶標)
懶標代表該區間應該被修改，但尚未操作
* 區間修改

#### 區間修改實作
經過含有懶標的節點時，順便推懶標
pull 之前確定左右子樹已被懶標作用

``` cpp=
typedef long long loli;
```
``` cpp=
void push(int l,int r,int v){
    summ[v]+=tag[v]*(r-l);
    if(r-l==1)
        return tag[v]=0,void();
    tag[2*v+1]+=tag[v];
    tag[2*v+2]+=tag[v];
    tag[v]=0;
}
```
``` cpp=
void pull(int l,int r,int v){
    if(r-l==1)
        return;
    int mid=(l+r)/2;
    push(l,mid,2*v+1);
    push(mid,r,2*v+2);
    summ[v]=summ[2*v+1]+summ[2*v+2];
}
```
**建樹**
遞迴把左右子樹建出來
拿建好的兩棵子樹合併出自己
``` cpp=
void build(int l,int r,int v=0){
    if(r-l==1){
        summ[v]=arr[l];
        return;
    }
    int mid=(l+r)/2;
    build(l,mid,2*v+1);
    build(mid,r,2*v+2);
    pull(l,r,v);
}
```
#### 區間查詢實作
在 [l,r) 中查詢 [L,R) 區間的答案
``` cpp=
loli query(int L,int R,int l,int r,int v=0){
    push(l,r,v);
    if(l==L && R==r) // 區間相同
        return summ[v];
    int mid=(l+r)/2;
    if(R<=mid) // 在左半邊
        return query(L,R,l,mid,2*v+1);
    else if(mid<=L) // 在右半邊
        return query(L,R,mid,r,2*v+2);
    else // 區間橫跨中點
        return query(L,mid,l,mid,2*v+1)
                +query(mid,R,mid,r,2*v+2);
}
```

#### 區間修改實作
在 [l,r) 中將 [L,R) 區間的值增加 val
找到 [L,R) 後，將其懶標打上 val
``` cpp
void update(int L,int R,loli val,int l,int r,int v=0){
    push(l,r,v);
    if(l==L && R==r){
        tag[v]+=val;
        push(l,r,v);
        return;
    }
    int mid=(l+r)/2;
    if(R<=mid)
        update(L,R,val,l,mid,2*v+1);
    else if(mid<=L)
        update(L,R,val,mid,r,2*v+2);
    else
        update(L,mid,val,l,mid,2*v+1),update(mid,R,val,mid,r,2*v+2);
    pull(l,r,v);
}
```

### 6. 課後練習
[Codeforces - DZY Loves Fibonacci Numbers](https://codeforces.com/contest/446/problem/C)
[Codeforces - Circular RMQ](https://codeforces.com/problemset/problem/52/C)
[Kattis - Just for Sidekicks](https://open.kattis.com/problems/justforsidekicks)
[Codeforces - Array Stabilization (GCD version)](https://codeforces.com/problemset/problem/1547/F)
[Codeforces - Not Equal on a Segment](https://codeforces.com/contest/622/problem/C)
[Codeforces - Boring Segments](https://codeforces.com/contest/1555/problem/E)
[Ab Initio](https://open.kattis.com/problems/abinitio)
[Ultra-QuickSort](https://open.kattis.com/problems/ultraquicksort)
[Codeforces - NGGYU](https://www.youtube.com/watch?v=dQw4w9WgXcQ)

---
<font size = 5>結束了！！！好耶！！！