The k-th Largest (并查集+线段树)

The k-th Largest Group

Time Limit: 2000ms
Memory Limit: 131072KB
64-bit integer IO format: %lld      Java class name: Main

Newman likes playing with cats. He possesses lots of cats in his home. Because the number of cats is really huge, Newman wants to group some of the cats. To do that, he first offers a number to each of the cat (1, 2, 3, …,n). Then he occasionally combines the group cat i is in and the group catj is in, thus creating a new group. On top of that, Newman wants to know the size of thek-th biggest group at any time. So, being a friend of Newman, can you help him?

Input

1st line: Two numbers N and M (1 ≤ N, M ≤ 200,000), namely the number of cats and the number of operations.

2nd to (m + 1)-th line: In each line, there is number C specifying the kind of operation Newman wants to do. IfC = 0, then there are two numbers i and j (1 ≤ i,jn) following indicating Newman wants to combine the group containing the two cats (in case these two cats are in the same group, just do nothing); IfC = 1, then there is only one number k (1 ≤ k ≤ the current number of groups) following indicating Newman wants to know the size of thek-th largest group.

Output

For every operation “1” in the input, output one number per line, specifying the size of the kth largest group.

Sample Input

10 10
0 1 2
1 4
0 3 4
1 2
0 5 6
1 1
0 7 8
1 1
0 9 10
1 1

Sample Output

1
2
2
2
2

/**********************************************************************************************************************************************************************

关于线段树的经验总结:

1.一系列的操作都用递归实现

2.无论构造还是更新线段树,都应该传入一个 "int node" 也就是虚拟的节点

还要传入 int left 和 int right  来作为要写入线段树的原始的数据域

3.构造函数:首先用if()语句判断结束条件   利用递归构造左右子树   回溯得到节点信息

其中的重要操作 1. 左子树 -> node * 2(node << 2)
                2. 右子树 -> node * 2 + 1(node << 2 | 1)
                3. 重要的结束条件 即 left == right

4.要理解每一个node对应一个独立的区间

**********************************************************************************************************************************************************************/

#include <cstdio>
#include <string.h>
int const Max = 200000;
int seg[Max * 4 + 10];
int num[Max +5];
int pre[Max + 5];
int n;
int findp(int x){//寻找祖先
    while(pre[x] != x){
        x = pre[x];
    }
    return x;
}
void build(int node, int l, int r){
    if(l == 1)  seg[node] = n;
    else    seg[node] = 0;
    if(l == r)  return;
    //构建左右子树
    build(node * 2, l, (l + r) / 2);
    build(node * 2 + 1, (l + r) / 2 + 1, r);
}
void update(int pos, int cc, int node, int l, int r){//假如要把i和j所在的团队合并,那么先找到i和j所在的区间段,把这个段的值各-1, 然后再找到i+j所在的区间段,把这个区间段的值+1
    seg[node] += cc;
    if(l == r) return;
    if(pos <= (l + r) / 2)
        update(pos, cc, node * 2, l, (l + r) / 2);
    else
        update(pos, cc, node * 2 + 1, (l + r) / 2 + 1, r);
}
int query(int pos, int node, int l, int r){//需要查询的是第k大的队伍
    if(l == r)
        return l;
    if(seg[node * 2 + 1] >= pos)
        return query(pos, node * 2 + 1, (l + r) / 2 + 1, r);
    else
        return query(pos - seg[node * 2 + 1], node * 2, l, (l + r) / 2);
}
int main(){
    int m, a, b, c, k;
    scanf("%d %d", &n, &m);
    for(int i = 1; i <= n; i++ ){
        pre[i] = i;
        num[i] = 1;
    }
    for(int i = 1; i <= m; i++){
        scanf("%d", &c);
        if(c == 0){
            scanf("%d %d", &a, &b);
            int x = findp(a);
            int y = findp(b);
            if(x == y) continue;
            update(num[x], -1, 1, 1, n);
            update(num[y], -1, 1, 1, n);
            update(num[x] + num[y], 1, 1, 1, n);
            num[x] += num[y];
            pre[y] = x;
        }
        else{
            scanf("%d", &k);
            printf("%d\n", query(k, 1, 1, n));
        }
    }
    return 0;
}





  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值