题目链接:http://poj.org/problem?id=2985
并查集合并小组,这个自然是不用说的,主要的问题是怎么求第K大值。
首先,线段树每一个结点存储的是组内元素的个数区间内有多少个组,初始化的时候所有的组中都是一个元素,所以当区间左端点为1的时候,结点的值为n,然后每次合并,原有小组的空间内结点值-1,合并后的新组空间内结点值+1,线段树这么维护完了以后,询问过程中,如果右子树的值大于k,那么第k大数一定在右子树当中。
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <algorithm>
using namespace std;
#define lson l, m, rt << 1
#define rson m + 1, r, rt << 1 | 1
const int maxn = 200010;
int sum[maxn << 2], n, q;
struct data{
int p, num;
}p[maxn];
void build(int l, int r, int rt){
if (l == 1) sum[rt] = n;
else sum[rt] = 0;
if (l == r) return;
int m = (l + r) >> 1;
build(lson);
build(rson);
}
void update(int x, int f, int l, int r, int rt){
if (!f) sum[rt] -= 1;
else sum[rt] += 1;
if (l == r) return;
int m = (l + r) >> 1;
if (x <= m) update(x, f, lson);
else update(x, f, rson);
}
void query(int k, int l, int r, int rt){
if (l == r){
printf("%d\n", l);
return;
}
int m = (l + r) >> 1;
if (sum[rt << 1 | 1] >= k) query(k, rson);
else query(k - sum[rt << 1 | 1], lson);
}
int find(int x){
if (p[x].p == x) return x;
int temp = p[x].p;
p[x].p = find(temp);
p[x].num = p[temp].num + p[x].num;
return p[x].p;
}
int main(){
while (~scanf("%d%d", &n, &q)){
for (int i = 0; i <= n; i++){
p[i].p = i; p[i].num = 1;
}
build(1, n, 1);
while (q--){
int op;
scanf("%d", &op);
if (op == 0){
int x, y;
scanf("%d%d", &x, &y);
x = find(x);
y = find(y);
if (x == y) continue;
update(p[x].num, 0, 1, n, 1);
update(p[y].num, 0, 1, n, 1);
update(p[x].num + p[y].num, 1, 1, n, 1);
p[y].p = x;
p[x].num += p[y].num;
}
if (op == 1){
int k;
scanf("%d", &k);
query(k, 1, n, 1);
}
}
}
return 0;
}