题目链接:http://poj.org/problem?id=2985
/**//*
题意:
给定N(N <= 200000)个操作,每个操作是以下两种形式:
0 x y : 合并x所在的组和y所在的组,如果x和y同组,不合并。
1 k:输出第k大的组的元素个数。
解法:
树状数组 + 并查集
思路:
将元素的个数对应树状数组的下标,每次合并操作,在树状
数组中对应两个集合大小的位置分别减1,两集合之后的位置+1,
询问操作只需要二分答案,然后每次统计比k大的数的个数判可行
即可。
*/
#include <iostream>
using namespace std;
#define maxn 200010
int c[maxn];
int prev[maxn], num[maxn];
int n;
int lowbit(int x) {
return x & (-x);
}
void add(int pos, int num) {
while(pos <= n) {
c[pos] += num;
pos += lowbit(pos);
}
}
int sum(int pos) {
int s = 0;
while(pos > 0) {
s += c[pos];
pos -= lowbit(pos);
}
return s;
}
int find(int x) {
int q = x;
while(x != prev[x]) {
x = prev[x];
}
return x;
}
void Union(int x, int y) {
x = find(x);
y = find(y);
if(x != y) {
if(num[x] == num[y])
add(num[x], -2);
else {
add(num[x], -1);
add(num[y], -1);
}
add(num[x] + num[y], 1);
if(num[x] < num[y]) {
prev[x] = y;
num[y] += num[x];
}else {
prev[y] = x;
num[x] += num[y];
}
}
}
int Query(int k) {
int l = 1;
int r = n;
int ans = 0;
int all = sum(n);
while(l <= r) {
int m = (l + r) >> 1;
if(k <= all - sum(m-1)) {
l = m + 1;
ans = m;
}else
r = m - 1;
}
return ans;
}
int main() {
int i, m;
int op, x, y;
while(scanf("%d %d", &n, &m) != EOF) {
for(i = 1; i <= n; i++) {
c[i] = 0;
prev[i] = i;
num[i] = 1;
}
add(1, n);
while(m--) {
scanf("%d", &op);
if(op == 0) {
scanf("%d %d", &x, &y);
Union(x, y);
}else {
scanf("%d", &x);
printf("%d\n", Query(x));
}
}
}
return 0;
}