题目大意:给你$n$个点,每个点有权值$k$,现有两种操作:
1. $B\;x\;y:$将$x,y$所在联通块合并
2. $Q\;x\;k:$查询第$x$个点所在联通块权值第$k$小是哪个数
题解:线段树合并,权值线段树上二分即可
卡点:无
C++ Code:
#include <cstdio>
#include <cctype>
namespace __IO {
namespace R {
int x, ch;
inline int read() {
ch = getchar();
while (isspace(ch)) ch = getchar();
for (x = ch & 15, ch = getchar(); isdigit(ch); ch = getchar()) x = x * 10 + (ch & 15);
return x;
}
inline char readc() {
ch = getchar();
while (!isalpha(ch)) ch = getchar();
return ch;
}
}
}
using __IO::R::read;
using __IO::R::readc;
#define maxn 100010
#define N (maxn * 50)
int n, m;
int w[maxn], ret[maxn];
int rt[maxn], lc[N], rc[N], sz[N], idx;
int f[maxn];
int find(int x) {return x == f[x] ? x : (f[x] = find(f[x]));}
void insert(int &rt, int l, int r, int num) {
if (!rt) rt = ++idx;
sz[rt]++;
if (l == r) return ;
int mid = l + r >> 1;
if (num <= mid) insert(lc[rt], l, mid, num);
else insert(rc[rt], mid + 1, r, num);
}
int __merge(int x, int y) {
if (!x || !y) return x | y;
sz[x] += sz[y];
lc[x] = __merge(lc[x], lc[y]);
rc[x] = __merge(rc[x], rc[y]);
return x;
}
void merge(int a, int b) {
int x = find(a), y = find(b);
if (x == y) return ;
rt[x] = __merge(rt[x], rt[y]);
f[y] = x;
}
int __query(int rt, int l, int r, int k) {
if (sz[rt] < k) return -1;
if (l == r) return l;
int mid = l + r >> 1;
if (sz[lc[rt]] >= k) return __query(lc[rt], l, mid, k);
else return __query(rc[rt], mid + 1, r, k - sz[lc[rt]]);
}
int query(int x, int k) {
x = find(x);
int res = __query(rt[x], 1, n, k);
return ~res ? ret[res] : -1;
}
int main() {
n = read(), m = read();
for (int i = 1; i <= n; i++) {
w[i] = read(), ret[w[i]] = f[i] = i;
insert(rt[i], 1, n, w[i]);
}
for (int i = 0, a, b; i < m; i++) {
a = read(), b = read();
merge(a, b);
}
int q = read();
while (q --> 0) {
char op = readc();
int x = read(), y = read();
if (op == 'B') merge(x, y);
else printf("%d\n", query(x, y));
}
return 0;
}