---
# System prepended metadata

title: Union of Segments (Basic)(線段樹應用)

---

# Union of Segments (Basic)(線段樹應用)

![Union of Segments (Basic)_1](https://hackmd.io/_uploads/S1ECCj7x1e.png)

![Union of Segments (Basic)_2](https://hackmd.io/_uploads/rkMgJnQl1l.png)


- 線段樹主要是可以維持一段區間，所以如果題目是要維持一段區間，可以套線段樹模板 
- 如果需要較短時間解題，可以開直接開線段樹陣列，不要用指標，且陣列Size可以開到題目最大輸入資料數4倍，而且不必在Struct Node內設Left和Right參數，因為線段樹往下Search，Parent為i，Left child為2\*i\(node<<1)，Right child: 2\*i+1(node<<1|1)
```cpp=
#define N 200005
struct segment{
    int count,length;
}seg[N<<4];
```
- 由於已經把segment tree用陣列開出來，且如果要維持的segment不用初始值或是初始值皆為0，就不用build tree
- 使用tag來表示此線段是有被cover以及次數，當tag>=1，表示此線段全部覆蓋，是0，表示沒有完全覆蓋https://blog.csdn.net/malloch/article/details/107999709
- Delete不是要刪去線段數節點，進入update or modify，把tag設定為0，就表示此線段沒有覆蓋到
- 此題題目意思是如果先給4個點在數線上，此時指令沒下4點都是獨立，Report應回傳0
![image](https://hackmd.io/_uploads/ryJLtPBxke.png)

* 當題目表示insert A B，表示把A B點接起來，此時如果Report此線段數是3-1=2
![image](https://hackmd.io/_uploads/rkEKEOBeyg.png)

* 此時如果下指令insert A C，則A C整段會包覆住，Report回５－１＝４
![image](https://hackmd.io/_uploads/B1GzoPHeyx.png)
* 如果下指令remove A B，由於A C整段會包覆住整段(此節點)，故Report還是5-1=4
![image](https://hackmd.io/_uploads/SkeFswrx1e.png)
:::warning 
* 使用分治法，分別往下尋找對應節點modify，終止條件為詢問區間在線段樹區間在ql<=l && qr>=r，此時表示此線段有被覆蓋(詢問區間大於此節點線段數區間)，tag值+1
* !!!條件成立後，繼續程式繼續往下，並持續把值往Root傳，有下列情況要討論:
    - tag>=1取該子節點整段(原資料data[r+1]-data[l]，r和l為分治法下的區間值，r+1表示恢復點做計算，e.g. 線段1表示1-2的線段，所以計算時候要改成data[2]-data[1])，所以帶入modify函式時候，區間(ql, qr)要帶入(ql, qr-1)
    - tag==0且l\==r表示已經到leaf，length=0
    - tag==0但l!=r，取左右子節點的length
- tag一方面也是記錄此線段的cover次數，當為0，表示此段已經沒有被整段cover，需要取該此節點的值(可理解為部分cover，例如1-5 tag為0但其值為2，表示其子節點3-5可能被cover且tag不為0，但是1-2沒有)
- 當tag已經有被標記，其子節點有變化，母節點取值時候依然會取整段區間，且不用下推給leaf (Lazy，需要做才更新) 
    https://blog.csdn.net/malloch/article/details/107999709
:::
- 只要取root-segment[1]的lenght就知道現在整的區間有多大，segment[0]捨棄不用
- segment本身節點編號以及存的值，不必依照對應的資料陣列或是去存取區間，例如node 2一定要存data[2]或是存1-3資訊，透過2分查找方式，就可以知道其node段應的區間(重要的是查找區間)
* 可用GDB設定斷點除錯 ![image](https://hackmd.io/_uploads/rkQ2WDzxke.png)
    - 簡介 Debugger 按鈕作用，在上面範例中，我稍微講一下這些按鈕在做啥。
    - 第一個按鈕：繼續（Continue），按下去後會持續跑，直到下個中斷點。
    - 第二個按鈕：不進入函式（Step over），按下去後，會到在目前這 Function 跑到下一個敘述。
    - 第三個按鈕：逐步執行（Step into），按下去後，會進入到每一部 Function 執行敘述並中斷。
    - 第四個按鈕：逐步執行（Step out），按下去後，會直接把 Function 執行完並中斷。
    - 第五個按鈕：重新起動，重跑 GDB 偵錯。
    - 第六個按鈕：停止，停止 GDB 偵錯。
    - Ref: https://blog.yangjerry.tw/vscode-cpp-2021-part2/

- 幾何題目可用ggb輔助畫圖幫忙
    - https://www.geogebra.org/classic?lang=zh-TW
- 程式碼參考如下(題目原文比較不好懂，多讀幾次) 
```cpp=
#include <iostream>
#include <vector>
#include <algorithm>
#include <set>
#include <tuple>
#define N 200005
using namespace std;

struct segment{
    int count,length; //count->tag, length->interval
}seg[N<<4];//segment tree
vector<int> coords;    // Coordinate mapping
int n;

void update(int node, int l, int r, int ql, int qr, int val) {
    if (ql <= l && r <= qr) {
        seg[node].count += val;
    } else {
            int mid = (l + r) / 2;
            if (ql <= mid) update(node * 2, l, mid, ql, qr, val);
            if (qr > mid) update(node * 2 + 1, mid + 1, r, ql, qr, val);
    }
        
        if (seg[node].count> 0) {
            seg[node].length = coords[r + 1] - coords[l]; /* Full segment is covered. When we want to get 
                                                            the segment length, r must be plused 1 to the point.*/
        } else if (l == r) {
            seg[node].length = 0;  // Leaf node, no coverage
        } else {
            seg[node].length = seg[node<<1].length + seg[node * 2 + 1].length;
        }
}

void insert(int l, int r) {
    update(1, 0, N, l, r - 1, 1);  // Insert the segment. Since we query the segment, r must be minused 1.
}

void remove(int l, int r) {
    update(1, 0, N, l, r - 1, -1); // Remove the segment. Since we query the segment, r must be minused 1.
}

int query() {
    return seg[1].length;  // Total length of the union of segments. seg[0] is unsed.
}


int main() {
    int n, q;
    cin >> n;
    vector<int> points(n);
    coords.resize(n);
    for (int i = 0; i < n; ++i) {
        cin >> points[i];
    }
    
    cin >> q;
    vector<tuple<string, int, int>> queries(q);

    // Read the queries
    for (int i = 0; i < q; ++i) {
        string type;
        int l, r;
        cin >> type;
        if (type != "report") {
            cin >> l >> r;
            --l, --r;  // Convert to 0-based indexing
        }
        queries[i] = {type, l, r};
    }

    sort(points.begin(), points.end());
    points.erase(unique(points.begin(), points.end()), points.end());
    coords=points;

    // Process the queries
    for (auto& _query : queries) {
        string type;
        int l, r;
        tie(type, l, r) = _query;

        if (type == "insert") {
            insert(l, r);
        } else if (type == "delete") {
            remove(l, r);
        } else if (type == "report") {
            cout <<query() << endl;
        }
    }

    return 0;
}


```

#### Ref: https://cp.wiwiho.me/segment-tree/
