# 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回5-1=4 ![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/