[洛谷P3224][HNOI2012]永无乡
题目大意:给你$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; }