1. 程式人生 > >luogu【P3377】 【模板】左偏樹

luogu【P3377】 【模板】左偏樹

 1 #include <cstdio>
 2 #include <cstring>
 3 #include <cstdlib>
 4 #include <cmath>
 5 #include <algorithm>
 6 #include <cctype>
 7 #include <iostream>
 8 #define For(i, l, r) for(int i = (l); i <= (int)(r); ++i)
 9 #define Fordown(i, r, l) for(int i = (r); i >= (int)(l); --i)
10
#define Set(a, v) memset(a, v, sizeof(a)) 11 using namespace std; 12 13 inline int read(){ 14 int x = 0, fh = 1; char ch; 15 for(; !isdigit(ch); ch = getchar()) if (ch == '-') fh = -1; 16 for(; isdigit(ch); ch = getchar()) x = (x<<1) + (x<<3) + (ch^'0'); 17 return x * fh;
18 } 19 20 struct node { 21 int lc, rc, val; //分別是left_child左兒子, right_child右兒子, value 鍵值 22 }; 23 const int max_node = 100100; //最大子節點個數 24 node lt[max_node]; //leftist_tree 左偏樹 25 int dist[max_node], fa[max_node]; //左偏樹距離dist:到最右葉子節點邊的條數 26 //並查集父親陣列fa 27 28 void make_tree (int x) { 29 lt[x].lc = lt[x].rc = dist[x] = 0
; //清空它的左右兒子和距離(新建一個樹) 30 } 31 32 int merge (int a, int b) { //合併兩個可以合併的樹 返回合併後的父節點 33 if (a == 0) return b; //a為空 返回b 34 if (b == 0) return a; //b為空 返回a 35 if (lt[a].val > lt[b].val) swap(a, b); //將根節點設為a(值較小的那個) 36 lt[a].rc = merge(lt[a].rc, b); fa[lt[a].rc] = a; //將b合併到a的右子樹上 並將右子樹父親設為a 37 if (dist[lt[a].rc] > dist[lt[a].lc]) swap(lt[a].lc, lt[a].rc); //右子節點距離大於左子節點 不符合左偏樹性質則交換左右子樹 38 if (lt[a].rc == 0) dist[a] = 0; //右子樹為空 距離為0 即直接到自己 39 else dist[a] = dist[lt[a].rc] + 1; //否則按照性質 距離為右子節點的距離+1 40 return a; //返回a節點 41 } 42 43 int find (int x) { 44 return fa[x] = fa[x] == x ? x : find(fa[x]); //並查集操作 45 } 46 47 void Merge (int a, int b) { //合併兩個數 48 int root_a = find(a), root_b = find(b); 49 if (root_a == root_b) return ; //在同一個堆裡就不合並 50 if (lt[a].val == 0 || lt[b].val == 0) return ; //這個數已被刪掉也不合並 51 // cout << "merge: " << root_a << ' ' << root_b << endl; 52 int tmp = merge(root_a, root_b); 53 fa[root_a] = fa[root_b] = tmp; //將兩個節點父親設為合併後的父親 54 } 55 56 void delete_min (int a) { 57 int root_a = find(a); //找到堆頂 58 if (lt[a].val == 0) {printf ("-1\n"); return;} //已被刪除 輸出-1 59 // cout << "find: " << root_a << endl; 60 printf ("%d\n", lt[root_a].val); //輸出堆頂的值 61 int tmp = merge (lt[root_a].lc, lt[root_a].rc); //合併這個堆頂的左右子樹 62 fa[lt[root_a].lc] = fa[lt[root_a].rc] = fa[root_a] = tmp; //將左右子樹和原來根節點的父親設為它們合併後的父親 63 lt[root_a].lc = lt[root_a].rc = lt[root_a].val = 0; //刪除堆頂 64 } 65 66 int main(){ 67 int n = read(), m = read(); 68 For (i, 1, n) { 69 lt[i].val = read(); 70 make_tree(i); 71 fa[i] = i; 72 } 73 while (m--) { 74 int opt = read(); 75 if (opt == 1) { 76 int a = read(), b = read(); 77 Merge(a, b); 78 } 79 else { 80 int a = read(); 81 delete_min(a); 82 } 83 } 84 }