首页 > 代码库 > UVA11987 - Almost Union-Find (并查集带删除)

UVA11987 - Almost Union-Find (并查集带删除)

UVA11987 - Almost Union-Find (并查集带删除)

题目链接

题目大意:给出三个操作: 1 p q 表示将p q 这两个数所在的集合合并在一起。2 p q表示将p这个数从原有的集合中拿出来放到q所在的集合中。3 p表示查询p所在的集合总共有几个元素,和是多少。

解题思路:并查集。只是并查集中并没有删除的操作。所以就需要将删除的这个点的影响降到0,也就是给删除的点申请一个新的id,以后都是用这个新的id来表示这个点,这样原来的那个集合里的点p就没意义,自然影响就为0.

代码:

#include <cstdio>
#include <cstring>

const int maxn = 2e5 + 5;

int n, m;
int parent[maxn], cnt[maxn], sum[maxn];
int id[maxn];
int dex;

void init () {

    for (int i = 0; i <= n; i++) {

        cnt[i] = 1;
        sum[i] = id[i] = parent[i] = i;
    }
    dex = n;
}

int getParent (int a) {

    return a == parent[a] ? a: parent[a] = getParent (parent[a]);
}

void Union (int a, int b) {

    int p = getParent (a);
    int q = getParent (b);
    if (p != q) {

        parent[p] = q;
        cnt[q] += cnt[p];
        sum[q] += sum[p];
    }
}

void move (int a) {

    int p = getParent (id[a]);
    cnt[p]--;
    sum[p] -= a;
    id[a] = ++dex;
    parent[dex] = dex;
    cnt[dex] = 1;
    sum[dex] = a;  
}

int main () {

    int type;
    int a, b;
    while (scanf ("%d%d", &n, &m) != EOF) {

        init();
        while (m--) {

            scanf ("%d", &type);
            if (type == 1) {

                scanf ("%d%d", &a, &b);
                Union (id[a], id[b]);
            } else if (type == 2) {

                scanf ("%d%d", &a, &b);
                int p = getParent (id[a]);
                int q = getParent (id[b]);
                if (p != q) {

                    move(a);
                    Union(id[a], id[b]);
                }
            } else {

                scanf ("%d", &a);
                int p = getParent (id[a]);
                printf ("%d %d\n", cnt[p], sum[p]);
            }
        }
    }
    return 0;
}

UVA11987 - Almost Union-Find (并查集带删除)