并查集——元素的合并与删除

一、问题描述 题目链接

有n个节点(1≤n≤100000),进行如下两种操作:

(1) M a b, 把a、b合并

(2)S  a, 把a分离出来

进行M(1≤M≤1000000)次操作,问最后有几个组?

二、解题思路

用并查集来实现,我们都知道并查集的合并操作很容易实现,而从集合中移出一个元素却很难。

这样想,移出元素相当于建立一个新的集合,我们只需将其指向一个独立的节点。这对于叶子节点容易操作,但如果是中间节点(或根节点),则会导致原集合断开。

我们将节点编号为0~n-1,将他们的根节点初始化为n~2n-1,这样保证了节点0~n-1都是叶子节点。同时将所有的移出节点分别指向2n~2n+m。最后查询0~n-1所在的不同的集合数。

三、代码实现

 1 #include<stdio.h>
 2 #include<iostream>
 3 #include<cstring>
 4 #include<cstdbool>
 5 #include<algorithm>
 6 using namespace std;
 7 
 8 const int maxn = 100000 + 10;
 9 const int maxk = 1000000 + 10;
10 int n, qcnt;
11 int fa[2 * maxn + maxk];
12 bool vis[2 * maxn + maxk];    //注意数组范围,2n+最大查询数
13 
14 void init()
15 {
16     memset(vis, false, sizeof(vis));
17     for (int i = 0; i < n; i++)
18         fa[i] = i + n;
19     for (int i = n; i < 2 * n + qcnt; i++)
20         fa[i] = i;
21 }
22 
23 int findset(int x)
24 {
25     if (x != fa[x])
26         return fa[x] = findset(fa[x]);
27     return fa[x];
28 }
29 
30 void unite(int x, int y)
31 {
32     int rx = findset(x);
33     int ry = findset(y);
34 
35     fa[rx] = ry;
36 }
37 
38 int main()
39 {
40     int kase = 0;
41     while (scanf("%d%d",&n,&qcnt) == 2 && (n || qcnt))    //题目描述有误,可能存在n>0,qcnt = 0的情况
42     {
43         init();
44         int cur = 2 * n;
45         while (qcnt--)
46         {
47             char order[3];
48             int a, b;
49             scanf("%s%d", order, &a);
50             if (order[0] == 'M')
51             {
52                 scanf("%d", &b);
53                 unite(a, b);
54             }
55             if (order[0] == 'S')
56             {
57                 fa[a] = cur;cur++;
58             }
59         }
60         int ans = 0;
61         for (int i = 0; i < n; i++)
62         {
63             int root = findset(i);
64             if (!vis[root])
65             {
66                 ans++;
67                 vis[root] = true;
68             }
69         }
70         printf("Case #%d: %d\n", ++kase, ans);
71     }
72 }

猜你喜欢

转载自www.cnblogs.com/lfri/p/9484005.html