bzoj 2733 永无乡 - 并查集 - 线段树
永无乡包含 n 座岛,编号从 1 到 n,每座岛都有自己的独一无二的重要度,按照重要度可 以将这 n 座岛排名,名次用 1 到 n 来表示。某些岛之间由巨大的桥连接,通过桥可以从一个岛 到达另一个岛。如果从岛 a 出发经过若干座(含 0 座)桥可以到达岛 b,则称岛 a 和岛 b 是连 通的。现在有两种操作:B x y 表示在岛 x 与岛 y 之间修建一座新桥。Q x k 表示询问当前与岛 x连通的所有岛中第 k 重要的是哪座岛,即所有与岛 x 连通的岛中重要度排名第 k 小的岛是哪 座,请你输出那个岛的编号。
Input
输入文件第一行是用空格隔开的两个正整数 n 和 m,分别 表示岛的个数以及一开始存在的桥数。接下来的一行是用空格隔开的 n 个数,依次描述从岛 1 到岛 n 的重要度排名。随后的 m 行每行是用空格隔开的两个正整数 ai 和 bi,表示一开始就存 在一座连接岛 ai 和岛 bi 的桥。后面剩下的部分描述操作,该部分的第一行是一个正整数 q, 表示一共有 q 个操作,接下来的 q 行依次描述每个操作,操作的格式如上所述,以大写字母 Q 或B 开始,后面跟两个不超过 n 的正整数,字母与数字以及两个数字之间用空格隔开。 对于 20%的数据 n≤1000,q≤1000 对于 100%的数据 n≤100000,m≤n,q≤300000
Output
对于每个 Q x k 操作都要依次输出一行,其中包含一个整数,表 示所询问岛屿的编号。如果该岛屿不存在,则输出-1。
Sample Input
5 1 4 3 2 5 1 1 2 7 Q 3 2 Q 2 1 B 2 3 B 1 5 Q 2 1 Q 2 4 Q 2 3
Sample Output
-1 2 5 1 2
题目大意 给出n个点和m条边,每个点有个点权,范围为1 ~ n,且没有重复。操作有两种,第一种是联通两个点,第二种是查询点i所在的连通块内第k小的点的编号。
显然会用到并查集,显然线段树合并。
然后我来讲讲线段树合并的时间复杂度证明。
一次线段树合并的实际代价是减少的节点个数。
开始有$O(n\log n)$个节点,最后只有$O(n)$个点,所以时间复杂度为$O(n\log n)$。
Code
1 /** 2 * bzoj 3 * Problem#2733 4 * Accepted 5 * Time:2352ms 6 * Memory:26304k 7 */ 8 #include <iostream> 9 #include <cstdio> 10 #include <ctime> 11 #include <cmath> 12 #include <cctype> 13 #include <cstring> 14 #include <cstdlib> 15 #include <fstream> 16 #include <sstream> 17 #include <algorithm> 18 #include <map> 19 #include <set> 20 #include <stack> 21 #include <queue> 22 #include <vector> 23 #include <stack> 24 #ifndef WIN32 25 #define Auto "%lld" 26 #else 27 #define Auto "%I64d" 28 #endif 29 using namespace std; 30 typedef bool boolean; 31 const signed int inf = (signed)((1u << 31) - 1); 32 const signed long long llf = (signed long long)((1ull << 63) - 1); 33 const double eps = 1e-6; 34 const int binary_limit = 128; 35 #define smin(a, b) a = min(a, b) 36 #define smax(a, b) a = max(a, b) 37 #define max3(a, b, c) max(a, max(b, c)) 38 #define min3(a, b, c) min(a, min(b, c)) 39 template<typename T> 40 inline boolean readInteger(T& u){ 41 char x; 42 int aFlag = 1; 43 while(!isdigit((x = getchar())) && x != '-' && x != -1); 44 if(x == -1) { 45 ungetc(x, stdin); 46 return false; 47 } 48 if(x == '-'){ 49 x = getchar(); 50 aFlag = -1; 51 } 52 for(u = x - '0'; isdigit((x = getchar())); u = (u * 10) + x - '0'); 53 ungetc(x, stdin); 54 u *= aFlag; 55 return true; 56 } 57 58 typedef class SegTreeNode { 59 public: 60 int s; 61 SegTreeNode *l, *r; 62 63 SegTreeNode():s(0), l(NULL), r(NULL) { } 64 SegTreeNode(int s, SegTreeNode* l, SegTreeNode* r):s(s), l(l), r(r) { } 65 66 inline void pushUp() { 67 s = l->s + r->s; 68 } 69 }SegTreeNode; 70 71 #define Limit 2000000 72 SegTreeNode pool[Limit]; 73 int top = 0; 74 SegTreeNode null = SegTreeNode(0, &null, &null); 75 76 inline SegTreeNode* newnode() { 77 if(top >= Limit) { 78 return new SegTreeNode(0, &null, &null); 79 } 80 pool[top].l = &null, pool[top].r = &null; 81 return pool + (top++); 82 } 83 84 #define null &null 85 86 typedef class union_found { 87 public: 88 int* f; 89 SegTreeNode** rts; 90 91 union_found():f(NULL), rts(NULL) { } 92 union_found(int n, int* lis) { 93 f = new int[(const int)(n + 1)]; 94 rts = new SegTreeNode*[(n + 1)]; 95 for(int i = 1; i <= n; i++) 96 f[i] = i; 97 for(int i = 1; i <= n; i++) { 98 rts[i] = null; 99 update(rts[i], 1, n, lis[i]); 100 } 101 } 102 103 void update(SegTreeNode*& node, int l, int r, int idx) { 104 if(node == null) node = newnode(); 105 if(l == idx && r == idx) { 106 node->s++; 107 return; 108 } 109 int mid = (l + r) >> 1; 110 if(idx <= mid) update(node->l, l, mid, idx); 111 else update(node->r, mid + 1, r, idx); 112 node->pushUp(); 113 } 114 115 void merge(SegTreeNode*& a, SegTreeNode*& b) { // Make segment tree b into a. 116 if(b == null) 117 return; 118 if(a == null) { 119 a = b; 120 return; 121 } 122 a->s += b->s; 123 merge(a->l, b->l); 124 merge(a->r, b->r); 125 a->pushUp(); 126 } 127 128 int kth(SegTreeNode* node, int l, int r, int k) { 129 if(node == null) 130 return -1; 131 if(l == r && k == 1) 132 return l; 133 int ls = node->l->s, mid = (l + r) >> 1; 134 if(k <= ls) 135 return kth(node->l, l, mid, k); 136 else 137 return kth(node->r, mid + 1, r, k - ls); 138 } 139 140 int find(int x) { 141 return (f[x] == x) ? (x) : (f[x] = find(f[x])); 142 } 143 144 void unit(int fa, int so) { 145 if(isConnected(fa, so)) return; 146 int ffa = find(fa); 147 int fso = find(so); 148 merge(rts[ffa], rts[fso]); 149 f[fso] = ffa; 150 } 151 152 boolean isConnected(int a, int b) { 153 return find(a) == find(b); 154 } 155 156 int query(int x, int n, int k) { 157 return kth(rts[find(x)], 1, n, k); 158 } 159 }union_found; 160 161 int n, m, q; 162 int* imp; 163 int* keyer; 164 union_found uf; 165 166 inline void init() { 167 readInteger(n); 168 readInteger(m); 169 imp = new int[(n + 1)]; 170 keyer = new int[(n + 1)]; 171 for(int i = 1; i <= n; i++) 172 readInteger(imp[i]), keyer[imp[i]] = i; 173 uf = union_found(n, imp); 174 for(int i = 1, a, b; i <= m; i++) { 175 readInteger(a); 176 readInteger(b); 177 uf.unit(a, b); 178 } 179 } 180 181 inline void solve() { 182 readInteger(q); 183 char buf[10]; 184 int a, b; 185 while(q--) { 186 scanf("%s%d%d", buf, &a, &b); 187 if(buf[0] == 'B') { 188 uf.unit(a, b); 189 } else { 190 int res = uf.query(a, n, b); 191 if(res == -1) 192 puts("-1"); 193 else 194 printf("%d\n", keyer[res]); 195 } 196 } 197 } 198 199 int main() { 200 init(); 201 solve(); 202 return 0; 203 }