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

来源:互联网 发布:江西省网络诈骗 编辑:程序博客网 时间:2024/06/06 05:54

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 numbersi 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 numberk (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 100 1 21 40 3 41 20 5 61 10 7 81 10 9 101 1

Sample Output

12222

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

关于线段树的经验总结:

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
原创粉丝点击