题目大意:有n个集合,提供三种操作
1 p q:将p所在的集合和q所在的集合并起来
2 p q:将p元素移到集合q所在的集合
3 p:求出p所在集合有多少个元素并输出这些元素的和
Sample Input
5 7
1 1 2 //集合1与2合并(集合操作)
2 3 4 //元素3放到元素4所在的集合中
1 3 5 //将[3,4]与[5]合并,得到[3,4,5]
3 4 //查询4所在的集合
2 4 1 //将元素4移动到1所在集合,得到[1,2,4]
3 4
3 3 //查询3所在的集合[3,5]
Sample Output //每查询一次,输出一次
3 12 //4所在的集合有3个元素,和为12
3 7
2 8
sol:开始时,n个点,n个集合,然后依次实现题目中的3种操作。在实现移动操作时,由于我们不知道该点在原集合中是整个集合的根还是叶子结点或其它中间结点,如果是叶子结点,操作简单,但如果是非叶子结点,特别是根结点,该集合中的其它结点的操作都与之息息相关,就不好操作了。所以我们采取的方法是将原点留在那里,只是消除它在原集合中根结点那的影响力,同时我们将该点的集合编号变更成其它的新编号,即开个虚点出来,该点单独成集合,然后与目标集合进行合并操作就好了。
代码实现:
1 #include <iostream>
2 #include <cstdio>
3 using namespace std;
4 #define maxn 200018
5 int father[maxn],idx[maxn],num[maxn];
6 long long int sum[maxn];
7 int n,m,cnt;
8 int find(int x)
9 {
10 if(x==father[x])return x;
11 return find(father[x]);
12 }
13 void init()
14 {
15 for(int i=1;i<=n;i++)
16 {
17 father[i]=idx[i]=sum[i]=i;
18 //idx[i]表示i当前的集合编号,sum[i]表示以i为根的集合的元素和
19 num[i]=1;//num[i]表示以i为根的集合元素个数
20 }
21 cnt=n;
22 }
23 void Union(int p,int q)
24 {
25 int pp=find(idx[p]),qq=find(idx[q]);
26 father[pp]=qq;
27 num[qq]+=num[pp];
28 sum[qq]+=sum[pp];
29 }
30 void Delete(int p)
31 {
32 int pp=idx[p];
33 //取出p所在集合的真实编号,因为存在删除操作,p所在的集合编号是不断变化的
34 sum[find(pp)]-=p; //消除p在原集合中的影响力
35 num[find(pp)]--;
36 idx[p]=++cnt; //新加一个集合出来,作为p的新集合编号
37 sum[idx[p]]=p; //以下是给p的新集合给初值
38 num[idx[p]]=1;
39 father[idx[p]]=idx[p];
40 }
41 int main()
42 {
43 while(scanf("%d%d",&n,&m)==2)
44 {
45 init();
46 int ope,p,q;
47 for(int i=1;i<=m;i++)
48 {
49 scanf("%d",&ope);
50 if(ope==1)
51 {
52 scanf("%d%d",&p,&q);
53 if(find(idx[p])==find(idx[q]))
54 continue;
55 else
56 Union(p,q);
57 }
58 else if(ope==2)
59 {
60 scanf("%d%d",&p,&q);
61 if(find(idx[p])!=find(idx[q]))
62 {
63 Delete(p);
64 Union(p,q);
65 }
66 }
67 else
68 {
69 int u;
70 scanf("%d",&u);
71 int fuck=find(idx[u]);
72 printf("%d %lld
",num[fuck],sum[fuck]);
73 }
74 }
75 }
76 return 0;
77 }