# 110 選手班 - 進階資結 ###### tags: `宜中資訊` `CP` Ccucumber12 2021.08.16 --- ## Outline - Lazy Propagation - Sweep Line - Dynamic Segment Tree - 2D Segment Tree - Persistent Segment Tree - Treap --- ## Lazy Propagation ---- ### Problem - 給定數列 $a_1, a_2, a_3, \dots a_N$,請支援以下操作 1. $\text{sum L R}$:計算 $a_L+a_{L+1}+\dots+a_R$ 2. $\text{add L R V}$:將$a_L+a_{L+1}+\dots+a_R$ 加上 $V$ - $N, Q \leq 10^5$ ---- ### Segment Tree - $\text{sum}$:$O(\log N)$ - $\text{add}$:$O((R - L)\log N)$ - Total: $O(QN\log N)$ \:cry: ---- ### KEY - 任意區間皆可在線段樹上表示為 $O(\log N)$ 個區間 - Proof: - 每個區間長度皆為 $2^i$ - 每個長度的區間最多兩個 - 每次操作只更動 $O(\log N)$ 個區間: - 若不須往下遞迴,則將修改的值暫時放在該區間 - 下次需往下遞迴時,再帶著它往下走 ---- ### Lazy Tag ```c= struct node { int val ; // current value int tag ; // value to be pushed down }; ``` ---- - 區間不完整包含: - 將該格 $\text{tag}$ 往下推 ($\text{push}$) - 往下遞迴 - 區間完整包含: - 修改該格的 $\text{val}$ - 暫時將修改的值記錄在 $\text{tag}$ ---- ### Push ```c= void push(int lb, int rb, int idx) { int len = (rb - lb + 1) / 2 ; seg[idx * 2].value += seg[idx].tag * len ; seg[idx*2+1].value += seg[idx].tag * len ; seg[idx * 2].tag += seg[idx].tag ; seg[idx*2+1].tag += seg[idx].tag ; seg[idx].tag = 0 ; } ``` ---- ### Pull ```c= void pull(int idx) { seg[idx].value = seg[idx*2].value + seg[idx*2+1].value ; } ``` ---- ### Lazy Tag - $\text{val}$ 已包含 $\text{tag}$ - $\text{val}$ 未包含 $\text{tag}$ ---- ```c= #include <bits/stdc++.h> using namespace std; struct node { int val ; // current value int tag ; // to be pushed down } ; node seg[1000010] ; void push(int lb, int rb, int idx) { int len = (rb - lb + 1) / 2; seg[idx * 2].val += seg[idx].tag * len ; seg[idx*2+1].val += seg[idx].tag * len ; seg[idx * 2].tag += seg[idx].tag ; seg[idx*2+1].tag += seg[idx].tag ; seg[idx].tag = 0 ; } void pull(int idx) { seg[idx].val = seg[idx*2].val + seg[idx*2+1].val ; } void modify(int l, int r, int v, int lb, int rb, int idx) { if(l <= lb && rb <= r) { seg[idx].val += v * (rb - lb + 1) ; seg[idx].tag += v ; return ; } push(lb, rb, idx) ; int mid = lb + rb >> 1 ; // (lb, mid) (mid+1, rb) if(l <= mid) modify(l, r, v, lb, mid, idx*2) ; if(mid < r) modify(l, r, v, mid+1, rb, idx) ; pull(idx) ; } int query(int l, int r, int lb, int rb, int idx) { if(l <= lb && rb <= r) return seg[idx].val ; push(lb, rb, idx) ; int mid = lb + rb >> 1 ; int ret = 0 ; if(l <= mid) ret += query(l, r, lb, mid, idx*2) ; if(mid < r) ret += query(l, r, mid+1, rb, idx*2+1) ; return ret ; } int main() { int n, q ; // ... } ``` --- ## Sweep Line ---- ### Problem - 給定 $N$ 個二維平面上的矩形,求聯集面積 - $N \leq 10^6$,$0 \leq \text{座標} \leq 10^6$ ---- ![](https://i.imgur.com/9cqc1Ka.png) ---- ### Solution - 假想有一條線由下往上掃 - 每次移動時維護涵蓋到的區間 - 將每個區間長乘上高度加總 ---- ![](https://i.imgur.com/dg3RsWb.png) ---- 1. 第 $i$ 個矩形進入掃描線:區間 $[xl_i, xr_i]$ 加 $1$ 2. 第 $i$ 個矩形離開掃描線:區間 $[xl_i, xr_i]$ 減 $1$ 3. 查詢整個數線 **非 $0$** 長度總和 ---- - 區間修改: $(1,2)$ - 區間查詢: $(3)$ - $\implies$ 懶標線段樹 ---- - $\text{cnt}$:該區間完整線段個數 - $\text{sum}$:該區間非$0$長度 ```c= struct node { int cnt, sum ; } ``` ---- ### Pull ```c= void pull(int lb, int rb, int idx) { if(seg[idx].cnt > 0) seg[idx].sum = rb - lb + 1 ; else seg[idx].sum = seg[idx*2].sum + seg[idx*2+1].sum ; } ``` ---- ### Push - 有需要嗎? - $\text{query}$:整個數線 - $\text{modify}$:刪除先前放入的區間 ---- ```c= // by Ccucumber12 #include <iostream> #include <algorithm> #include <cmath> #include <bitset> #include <cstring> #include <string.h> #include <sstream> #include <iomanip> #include <queue> #include <stack> #include <deque> #include <map> #include <set> #include <unordered_set> #include <unordered_map> #include <tuple> #include <vector> #include <random> #include <chrono> using namespace std; #define F first #define S second #define siz(v) ((int)v.size()) #define rs(n) resize(n) #define ALL(v) v.begin(),v.end() #define reset(v) memset((v),0,sizeof(v)) #define EB emplace_back #define MP make_pair #define rep(i,n) for(int i=0;i<(n);i++) #define rep1(i,n) for(int i=1;i<=(n);i++) #define REP(i,a,b) for(int i=(a);i<=(b);i++) #define debug(x) cout << " > " << #x << ':' << x << endl; #define kiyohime ios::sync_with_stdio(false);cin.tie(0); #define endl '\n' using ll = unsigned long long; using pii = pair<int,int>; using pll = pair<ll,ll>; template<typename T> using vec = vector<T>; template<typename T> using Prior = priority_queue<T>; template<typename T> using prior = priority_queue<T, vec<T>, greater<T>>; const int INF = 1e9; const ll LLINF = (ll)4*1e18; const ll MOD = 1e9+7; const double PI = 3.14159265358; const double EPS = 1e-8; const int xx[8] = {0,1,0,-1,1,1,-1,-1}; const int yy[8] = {1,0,-1,0,1,-1,-1,1}; void pmod(ll &a, ll b) {a = (a+b)%MOD;} void mmod(ll &a, ll b) {a = (a-b+MOD)%MOD;} void tmod(ll &a, ll b) {a = (a*b)%MOD;} mt19937 rng(chrono::steady_clock::now().time_since_epoch().count()); ll POW(ll a, ll b) {ll res=1; do{if(b%2)tmod(res,a);tmod(a,a);}while(b>>=1); return res;} template<typename T> T gcd(T a, T b) {return b == 0 ? a : gcd(b, a%b);}; template<typename T> void amax(T &a, T b) {if(a < b) a = b;} template<typename T> void amin(T &a, T b) {if(a > b) a = b;} #define N 100010 struct node{ node *lch, *rch; int val, sum; node () { lch = rch = nullptr; val = sum = 0; } void pull(){ sum = 0; if(lch) sum += lch -> sum; if(rch) sum += rch -> sum; } void modify(int l, int r, int lb, int rb, int v){ if(l <= lb && rb <= r){ val += v; if(val) sum = rb - lb + 1; else pull(); return ; } int mid = lb + rb >> 1; if(!lch) lch = new node(); if(!rch) rch = new node(); if(l <= mid) lch -> modify(l, r, lb, mid, v); if(mid < r) rch -> modify(l, r, mid+1, rb, v); if(val) sum = rb - lb + 1; else pull(); } }; struct segment{ int L, R, H, V; }; void solve(){ int n; cin >> n; vec<segment> v; rep(i, n){ int l, r, u, d; cin >> l >> r >> d >> u; v.EB(segment{l+1, r, d, 1}); v.EB(segment{l+1, r, u, -1}); } sort(ALL(v), [](segment a, segment b){ return a.H < b.H; }); node *root = new node(); int cur = v.front().H; ll ans = 0; for(auto i:v){ if(i.H != cur){ ans += (ll)root -> sum * (i.H - cur); cur = i.H; } root -> modify(i.L, i.R, 1, 1e6, i.V); } ans += (ll)root -> sum * (v.back().H - cur); cout << ans << endl; } int main(){ // kiyohime int T = 1; // cin >> T; while(T--){ solve(); } } ``` --- ## Dynamic Segment Tree ---- ### Problem - 給定 $N$ 個二維平面上的矩形,求聯集面積 - $N \leq 10^6$,$|\text{座標}| \leq 10^9$ ---- ### Discretize - 離散化 - 紀錄區間原始長度當成權重 - 塞進線段樹 ---- ### Copy on Write - 只記錄需要 / 經過的點 - 以指標實作 - 每次操作:$\mathcal{O}(\log C)$ ---- ### Node ```c= struct node { int val ; node *lch, *rch ; void pull() { ... } void modify() { ... } int query() { ... } } ``` ---- ### Pull ```c= void pull() { val = 0 ; if(lch) val += lch -> val ; if(rch) val += rch -> val ; } ``` ---- ### Modify ```c= void modify(int p, int v, int lb, int rb) { if(lb == rb) { val = v ; return ; } if(!lch) lch = new node() ; if(!rch) rch = new node() ; int mid = lb + rb >> 1 ; if(p <= mid) lch -> modify(p, v, lb, mid) ; if(mid < p) rch -> modify(p, v, mid+1, rb) ; pull() ; } ``` ---- ### Query ```c= int query(int l, int r, int lb, int rb) { if(l <= lb && rb <= r) return val ; int mid = lb + rb >> 1 ; int ret = 0 ; if(l <= mid && lch) ret += lch -> query(l, r, lb, mid) ; if(mid < r && rch) ret += rch -> query(l, r, mid+1, rb) ; return ret ; } ``` ---- ### KEY - 拜訪指標前確保存在,避免戳到 $\text{nullptr}$ - 將要做的事好好整理在副函式裡 - 訪問指標內物件記得使用 `->` - 操作完小孩記得 $\text{pull}$ 一下 ---- ### Complexity - Time: $\mathcal{O}(\log C)$ - Space: $\mathcal{O}(\log C)$ new nodes / modify --- ## 2D Segment Tree ---- ### Problem - 給定$N \times N$ 平面,初始值皆為 $0$,請支援以下操作 1. $\text{add x y v}$:將座標 $(x,y)$ 加上 $v$ 2. $\text{query x1 y1 x2 y2}$:詢問該矩形總和 - $N \leq 10^9$,$Q \leq 10^5$ ---- 對於其中一個維度的每一格開一個線段樹 - Add: $\mathcal{O}(\log N)$ - Query: $\mathcal{O}(N\log N)$ ---- - 能否減少 Query 的時間複雜度? - 將第一個維度多個區間合併在一起? - 線段樹包線段樹?(樹套樹) ---- - 將第一個維度切成多個區間 (線段樹) - 每個線段樹所代表的區間視為一格 - 對第二維度做線段樹 ---- ![](https://i.imgur.com/VUiYlp0.png) ---- ### Query - 將第一個維度切成 $\mathcal{O}(\log C)$ 個區間(線段樹) - 對於每個區間(線段樹),Query第二個維度所需的長度 - 將每個區間的答案合併 - $\mathcal{O}(\log^2 C)$ ---- ### Add - 對於任意座標,第一維度共有 $\mathcal{O}(\log N)$ 個區間包含 - 每個區間再花 $\mathcal{O}(\log N)$ 做修改 - $\mathcal{O}(\log^2 C)$ ---- ### Complexity - Time Complexity: - Add: $\mathcal{O}(\log^2 C)$ - Query: $\mathcal{O}(\log^2 C)$ - Space Complexity: $\mathcal{O}(\log^2 C)$ ---- ```c= struct seg1D { int val = 0; seg1D *lch = nullptr, *rch = nullptr ; void pull() { val = 0 ; if(lch) amax(val, lch -> val) ; if(rch) amax(val, rch -> val) ; } void modify(int x, int p, int lb = 0, int rb = N) { if(lb == rb) { amax(val, p) ; return ; } int mid = lb + rb >> 1; if(x <= mid) { if(!lch) lch = new seg1D ; lch -> modify(x, p, lb, mid) ; } if(mid < x) { if(!rch) rch = new seg1D ; rch -> modify(x, p, mid+1, rb) ; } pull() ; } int query(int l, int r, int lb = 0, int rb = N) { if(l <= lb && rb <= r) return val ; int mid = lb + rb >> 1 ; int ret = 0 ; if(l <= mid && lch) amax(ret, lch -> query(l, r, lb, mid)) ; if(mid < r && rch) amax(ret, rch -> query(l, r, mid+1, rb)) ; return ret ; } }; struct seg2D { seg1D seg; seg2D *lch = nullptr, *rch = nullptr; void modify(int x, int y, int p, int lb = 0, int rb = N) { seg.modify(y, p) ; if(lb == rb) return ; int mid = lb + rb >> 1 ; if(x <= mid) { if(!lch) lch = new seg2D ; lch -> modify(x, y, p, lb, mid) ; } if(mid < x) { if(!rch) rch = new seg2D ; rch -> modify(x, y, p, mid+1, rb) ; } } int query(int xl, int xr, int yl, int yr, int lb = 0, int rb = N) { if(xr < xl || yr < yl) return 0 ; if(xl <= lb && rb <= xr) return seg.query(yl, yr) ; int mid = lb + rb >> 1 ; int ret = 0 ; if(xl <= mid && lch) amax(ret, lch -> query(xl, xr, yl, yr, lb, mid)) ; if(mid < xr && rch) amax(ret, rch -> query(xl, xr, yl, yr, mid+1, rb)) ; return ret ; } }; ``` ---- ### 三維偏序 - 給定 $N$ 個物件,每個物件有三個參數 $X_i, Y_i, Z_i$ - 請問最多可以選幾個物件,使得任意排序後,三個參數皆嚴格遞增 ---- #### 二維偏序 - 最長嚴格遞增子序列 - 依照 $X$ 排序 - 對於每個物件查詢 $[1,Y-1]$ 之間最大DP值 - 將答案更新在 $Y$ - BIT / Segment Tree ---- - 依照 $X$ 排序 - 對於每個物件查詢 $[(1,1) ~ (Y-1, Z-1)]$ 矩形內最大DP值 - 將答案更新再 $(Y,Z)$ - 2D Segment Tree ---- ### D維線段樹 - 樹套樹套樹... - 每次顆線段樹維護一個維度 - Time Complexity: $\mathcal{O}(\log^D C)$ ---- ### Lazy Propagation - 只支援永久懶標:不須往下push - 其他方案: - Quadtree - KD Tree --- ## Persistent Segment Tree ---- ### Problem - 給定數列 $a_1, a_2, a_3, \dots a_N$,請支援以下操作 1. $\text{add x v}$:將 $a_x$ 加上 $v$ 2. $\text{query l r k}$: 詢問第 $k$ 次修改後 ---- ### Offline - 正常操作,在第 $k$ 次操作後記錄需要的答案 - 整理後輸出 - $\mathcal{O}(Q\log N)$ ---- ### Online - 回到過去? - 紀錄每個版本的線段樹 - Space Complexity: $\mathcal{O}(QN)$ ---- ### Copy on Write - 只備份改變的那些點 - 每一個新的版本代表一個新的root - 每次修改 $\mathcal{O}(\log N)$ 個節點 - 總共增加 $\mathcal{O}(Q\log N)$ 個空間 - Space Complexity: $\mathcal{O}(N + Q\log N)$ ---- ### Persistent Segment Tree - 持久化線段樹 - 主席樹 ---- ![](https://i.imgur.com/bpmZPnk.png) ---- ```cpp= struct node{ node *lch, *rch; int val; node () { lch = rch = nullptr; val = 0; } void pull(){ val = lch -> val + rch -> val; } }; void build(int l, int r, node *&p){ p = new node(); if(l == r) return ; int mid = (l + r) >> 1; build(l, mid, p -> lch); build(mid+1, r, p -> rch); p -> pull(); } void modify(int pos, int lb, int rb, node *pre, node *&cur){ cur = new node(); if(lb == rb) { cur -> val = pre -> val + 1; return ; } cur -> lch = pre -> lch; cur -> rch = pre -> rch; int mid = (lb + rb) >> 1; if(pos <= mid) modify(pos, lb, mid, pre -> lch, cur -> lch); if(mid < pos) modify(pos, mid+1, rb, pre -> rch, cur -> rch); cur -> pull(); } int query(int val, int lb, int rb, node *l, node *r){ if(lb == rb) return lb; int k = r->lch->val - l->lch->val; int mid = (lb + rb) >> 1; if(val <= k) return query(val, lb, mid, l->lch, r->lch); else return query(val-k, mid+1, rb, l->rch, r->rch); } ``` ---- ### 區間第 k 小 - 給定數列 $a_1, a_2, a_3, \dots a_N$,請支援以下操作 1. $\text{query l r k}$:詢問區間 $[a_l,\ a_r]$ 中第 $k$ 小的數字 - $N,Q,a_i \leq 10^6$ ---- 只詢問一次 $[1, N]$ - 相對位置不重要 - 各個數字有多少個 - **值域線段樹** ---- ### 值域線段樹 - 葉節點 $\text{seg}[i]$ 代表數字 $i$ 有多少個 - 區間 $\text{seg}[L,R]$ 代表數字介於 $[L,R]$ 有多少個 - 第 $k$ 小:葉節點 $x$ 的左邊累積到 $k-1$ - **二分搜** ---- 只詢問區間 $[1, R]$ - 依序插入第 $i$ 個數 - 線段樹紀錄每次插入第 $i$ 個數的結果 - **持久化線段樹** ---- 詢問區間 $[L, R]$ - 第 $L$ 個版本到第 $R$ 個版本 - 這些版本的數字個數 - 版本$R$ $-$ 版本$(L-1)$ ---- ```c= // by Ccucumber12 #include <bits/stdc++.h> using namespace std; #define F first #define S second #define siz(v) ((int)v.size()) #define rs(n) resize(n) #define ALL(v) v.begin(),v.end() #define reset(v) memset((v),0,sizeof(v)) #define EB emplace_back #define MP make_pair #define rep(i,n) for(int i=0;i<(n);i++) #define rep1(i,n) for(int i=1;i<=(n);i++) #define REP(i,a,b) for(int i=(a);i<=(b);i++) #define debug(x) cout << " > " << #x << ':' << x << endl; #define kiyohime ios::sync_with_stdio(false);cin.tie(0); #define endl '\n' using ll = unsigned long long; using pii = pair<int,int>; using pll = pair<ll,ll>; template<typename T> using vec = vector<T>; template<typename T> using Prior = priority_queue<T>; template<typename T> using prior = priority_queue<T, vec<T>, greater<T>>; const int INF = 1e9; const ll LLINF = (ll)4*1e18; const ll MOD = 1e9+7; const double PI = 3.14159265358; const double EPS = 1e-8; const int xx[8] = {0,1,0,-1,1,1,-1,-1}; const int yy[8] = {1,0,-1,0,1,-1,-1,1}; void pmod(ll &a, ll b) {a = (a+b)%MOD;} void mmod(ll &a, ll b) {a = (a-b+MOD)%MOD;} void tmod(ll &a, ll b) {a = (a*b)%MOD;} mt19937 rng(chrono::steady_clock::now().time_since_epoch().count()); ll POW(ll a, ll b) {ll res=1; do{if(b%2)tmod(res,a);tmod(a,a);}while(b>>=1); return res;} template<typename T> T gcd(T a, T b) {return b == 0 ? a : gcd(b, a%b);}; template<typename T> void amax(T &a, T b) {if(a < b) a = b;} template<typename T> void amin(T &a, T b) {if(a > b) a = b;} #define N 100010 vec<int> discrete(vec<int> &v){ vec<int> dct; for(auto i:v) dct.EB(i); sort(ALL(dct)); dct.rs(unique(ALL(dct)) - dct.begin()); for(auto &i:v) i = int(lower_bound(ALL(dct), i) - dct.begin() + 1); return dct; } struct node{ node *lch, *rch; int val; node () { lch = rch = nullptr; val = 0; } void pull(){ val = lch -> val + rch -> val; } }; void build(int l, int r, node *&p){ p = new node(); if(l == r) return ; int mid = (l + r) >> 1; build(l, mid, p -> lch); build(mid+1, r, p -> rch); } void modify(int pos, int lb, int rb, node *pre, node *&cur){ cur = new node(); if(lb == rb) { cur -> val = pre -> val + 1; return ; } cur -> lch = pre -> lch; cur -> rch = pre -> rch; int mid = (lb + rb) >> 1; if(pos <= mid) modify(pos, lb, mid, pre -> lch, cur -> lch); if(mid < pos) modify(pos, mid+1, rb, pre -> rch, cur -> rch); cur -> pull(); } int query(int val, int lb, int rb, node *l, node *r){ if(lb == rb) return lb; int k = r->lch->val - l->lch->val; int mid = (lb + rb) >> 1; if(val <= k) return query(val, lb, mid, l->lch, r->lch); else return query(val-k, mid+1, rb, l->rch, r->rch); } void solve(){ int n, m; cin >> n >> m; vec<int> v(n), dct; for(auto &i:v) cin >> i; dct = discrete(v); vec<node*> ver(n+1); int mxn = 1; while(mxn < siz(dct)) mxn <<= 1; build(1, mxn, ver[0]); rep(i, n) modify(v[i], 1, mxn, ver[i], ver[i+1]); int ipl, ipr, ipk; while(m--){ cin >> ipl >> ipr >> ipk; cout << dct[query(ipk, 1, mxn, ver[ipl-1], ver[ipr]) - 1] << endl; } } int main(){ // kiyohime int T = 1; // cin >> T; while(T--){ solve(); } } ``` --- ## Treap ---- - Tree + Heap - 平衡樹 - Merge-Split Treap - Splay - ... ---- ### Binary Search Tree - Time Complexity: $\mathcal{O}(h)$ - 高度太高 - 加入變數使得高度期望是 $\mathcal{O}(\log N)$ ---- ### Key & Pri - Key: 存入的數字,維護BST - 任意Key大於等於左子樹的Key - 任意Key小於等於右子樹的Key - Pri: 隨機變數,維護heap - 任意Pri大於所有子樹的Pri ---- ### 隨機Pri - 若隨機給予 Pri 的值 - 期望高度為 $\mathcal{O}(\log N)$ - Proof: <\<Introduction to Algorithms>> ---- ### 其他功能 - size: 區間數字個數 - count: 數字存在個數 - rank: 詢問數字第幾大 - kth: 詢問第 $k$ 大的數字 - 各種集合操作 - ... ---- ```c= struct Treap { struct node { int val, pri, sz; node *lch, *rch; node (int _val) { val = _val ; sz = 1 ; pri = rng() ; lch = rch = nullptr ; } void pull() { sz = 1 ; if(lch) sz += lch -> sz ; if(rch) sz += rch -> sz ; } }; node *rt = nullptr ; int fs (node *a) { return a ? a -> sz : 0 ; } node *merge(node *a, node *b) { if(!a || !b) return a ? a : b ; if(a -> pri < b -> pri) { a -> rch = merge(a -> rch, b) ; a -> pull() ; return a ; } else { b -> lch = merge(a, b -> lch) ; b -> pull() ; return b ; } } void split(node *T, node *&a, node *&b, int p) { if(!T) { a = b = nullptr ; return ; } if(T -> val > p) { b = T ; split(T -> lch, a, b -> lch, p) ; b -> pull() ; } else { a = T ; split(T -> rch, a -> rch, b, p) ; a -> pull() ; } } void splitSz(node *T, node *&a, node *&b, int p) { if(!T) { a = b = nullptr ; return ; } if(fs(T -> lch) < p) { a = T ; splitSz(T -> rch, a -> rch, b, p - fs(T -> lch) - 1); a -> pull() ; } else { b = T ; splitSz(T -> lch, a, b -> lch, p) ; b -> pull() ; } } void insert(int p) { node *a = new node(p), *b; split(rt, rt, b, p) ; rt = merge(rt, a) ; rt = merge(rt, b) ; } void erase(int p) { node *a, *b ; split(rt, rt, a, p - 1) ; splitSz(a, a, b, 1) ; delete(a) ; rt = merge(rt, b) ; } int count(int p) { node *a, *b ; split(rt, rt, b, p) ; split(rt, rt, a, p - 1) ; int ret = fs(a) ; rt = merge(rt, a) ; rt = merge(rt, b) ; return ret ; } int rank(int p) { node *a, *b; split(rt, rt, b, p) ; split(rt, rt, a, p - 1) ; int ret = fs(rt) + 1 ; rt = merge(rt, a); rt = merge(rt, b); return ret ; } int kth(int p) { node *a, *b ; splitSz(rt, rt, b, p) ; splitSz(rt, rt, a, p - 1) ; int ret = a -> val ; rt = merge(rt, a) ; rt = merge(rt, b) ; return ret ; } int prev(int p) { node *a, *b ; split(rt, rt, b, p - 1) ; splitSz(rt, rt, a, fs(rt) - 1) ; int ret = a -> val ; rt = merge(rt, a) ; rt = merge(rt, b) ; return ret ; } int next(int p) { node *a, *b ; split(rt, rt, a, p) ; splitSz(a, a, b, 1) ; int ret = a -> val ; a = merge(a, b) ; rt = merge(rt, a) ; return ret ; } }; ``` ---- ### 處理線性資料 - Insert the data by merging the treaps one by one - Inorder will be the linear data, while the pri will make it balanced. ---- ### 功能 - 將任意區間切下 - 將任意區間插入 - 將任意區間反轉 - 將任意區間旋轉 - 各種區間操作 - ... ---- ```c= struct Treap{ Treap *l, *r; int val, pri, size; bool revTag; Treap (int _v) : l (nullptr), r (nullptr), val (_v), pri (rng()), size (1), revTag (false) {} void rev(){ swap(l, r); revTag ^= 1; } void push(){ if(revTag){ if(l) l -> rev(); if(r) r -> rev(); revTag = false; } } void pull(){ size = 1; if(l) size += l -> size; if(r) size += r -> size; } }; int getSize(Treap *p){ return p ? p -> size : 0; } Treap *merge (Treap *a, Treap *b){ if(!a || !b) return a ? a : b; if(a -> pri < b -> pri){ a -> push(); a -> r = merge(a -> r, b); a -> pull(); return a; } else { b -> push(); b -> l = merge(a, b -> l); b -> pull(); return b; } } void splitSize (Treap *p, Treap *&a, Treap *&b, int k){ if(!p) { a = b = nullptr; return; } p -> push(); if(getSize(p -> l) < k){ a = p; splitSize(p -> r, a -> r, b, k - getSize(p->l) - 1); a -> pull(); } else { b = p; splitSize(p -> l, a, b -> l, k); b -> pull(); } } void rev(Treap *&p, int l, int r){ Treap *a, *b, *c; splitSize(p, a, c, r); splitSize(a, a, b, l-1); b -> rev(); p = merge(merge(a, b), c); } void dfs(Treap *p){ // in-order if(!p) return ; p -> push(); if(p -> l) dfs(p -> l); cout << p -> val << ' ' ; if(p -> r) dfs(p -> r); } ``` --- ## Credit - 演算法筆記 - http://pisces.ck.tp.edu.tw/~peng/index.php?action=showfile&file=f220f2d6b33ae091978ebf59d2af5908bc8190b51
{"metaMigratedAt":"2023-06-16T07:18:22.480Z","metaMigratedFrom":"Content","title":"110 選手班 - 進階資結","breaks":true,"description":"Ccucumber122021.08.16","contributors":"[{\"id\":\"3978a08d-c47c-4560-b04d-dfbd8e71d0a3\",\"add\":2,\"del\":0},{\"id\":\"45262441-89e4-46ca-959b-c0d6fadb64e2\",\"add\":22324,\"del\":544}]"}
    249 views
   owned this note