题
I hope you know the beautiful Union-Find structure. In this problem, you’re to implement something
similar, but not identical.
The data structure you need to write is also a collection of disjoint sets, supporting 3 operations:
1 p q Union the sets containing p and q. If p and q are already in the same set,
ignore this command.
2 p q Move p to the set containing q. If p and q are already in the same set,
ignore this command.
3 p Return the number of elements and the sum of elements in the set containing
p.
Initially, the collection contains n sets: {1}, {2}, {3}, . . . , {n}.
Input
There are several test cases. Each test case begins with a line containing two integers n and m
(1 ≤ n, m ≤ 100, 000), the number of integers, and the number of commands. Each of the next m lines
contains a command. For every operation, 1 ≤ p, q ≤ n. The input is terminated by end-of-file (EOF).
Output
For each type-3 command, output 2 integers: the number of elements and the sum of elements.
Explanation
Initially: {1}, {2}, {3}, {4}, {5}
Collection after operation 1 1 2: {1,2}, {3}, {4}, {5}
Collection after operation 2 3 4: {1,2}, {3,4}, {5} (we omit the empty set that is produced when
taking out 3 from {3})
Collection after operation 1 3 5: {1,2}, {3,4,5}
Collection after operation 2 4 1: {1,2,4}, {3,5}
翻译
有几个测试用例。每个测试用例从一行开始,该行包含两个整数n和m
(1 ≤ n、 m≤ 100000),整数个数 和 命令数
1 p q 并集包含p和q的集合。如果p和q已经在同一个集合中,
忽略此命令。
2 p q 将p移动到包含q的集合。如果p和q已经在同一个集合中,
忽略此命令。
3 p 返回包含元素的集合中元素的数量和元素的总和
P
最初,集合包含n个集合:{1}、{2}、{3}、{n} 。
Sample Input
5 7
1 1 2
2 3 4
1 3 5
3 4
2 4 1
3 4
3 3
Sample Output
3 12
3 7
2 8
解释
最初:{1}、{2}、{3}、{4}、{5}
操作1 1 2 后的集合:{1,2},{3},{4},{5}
操作2 3 4 之后的集合:{1,2},{3,4},{5}
操作1 3 5之后的集合:{1,2},{3,4,5}
操作2 4 1之后的集合:{1,2,4},{3,5}
代码
#include "string.h"
#include "stdio.h"
int a[100000*2+99];//祖先
int sum[100000+9];//集合的和
int su[100000+9];//集合的数量
int idx[100000+9]; //转换量 ,idx[i]的值是i真正的代表值
int n,number;
void ppp()
{
for(int i=1; i<=n; i++)
{
a[i]=i;
sum[i]=i;
idx[i]=i;//这个数组很重要,下面解释
su[i]=1;
}
number=n+1;
}
int ff(int x)
{
if(a[x]==x) return x;
return a[x]=ff(a[x]);
}
void change(int x,int ans) //ans:x以前的祖先
{
sum[ans]-=x;
su[ans]--;
idx[x]=number++;//idx[x],x的代表值,代表x,
a[idx[x]]=idx[x];//idx[x]的祖先为idx[x]
sum[idx[x]]=x;
su[idx[x]]=1;
}
int main()
{
int i,m,j,k,l;
while(~scanf("%d%d",&n,&m))
{
ppp();
while(m--)
{
int n1,n2;
scanf("%d",&k);
if(k==1)
{
scanf("%d%d",&n1,&n2);
int m1=ff(idx[n1]);
int m2=ff(idx[n2]);
if(m1==m2)
continue;
else
{
a[m1]=m2;
sum[m2]+=sum[m1];
su[m2]+=su[m1];
}
}
else if(k==2)
{
scanf("%d%d",&n1,&n2);
int m1=ff(idx[n1]);
int m2=ff(idx[n2]);
if(m1==m2)
continue;
else
{
change(n1,m1);
m1=ff(idx[n1]);
a[m1]=m2;
sum[m2]+=sum[m1];
su[m2]+=su[m1];
}
}
else
{
scanf("%d",&n1);
int m1=ff(idx[n1]);
printf("%d %d\n",su[m1],sum[m1]);
}
}
}
}
解释,思路
idx[]重要
两个集合 {1,2,3} {4}
{1,2,3} a[1]=2,a[2]=3,a[3]=3; 大王为 3
{4} a[4]=4 大王为 4
指令:2 2 4 把 2移到包含4的集合中
如果我们直a[2]=4 此时sum[4]=sum[4]+2=6,su[4]=su[4]+1=2
sum[3]=sum[3]-2=4,su[3]=su[3]-1=2
两个集合变为 {1,3} {2,4} a[1]=2, a[2]=4, a[3]=3, a[4]=4
指令:3 1; a[1]=2,a[2]=4,a[4]=4,我们会认为 1 的大王为 4
输出 su[4] sum[4] 即 2 6;
答案明显是错的 应该是 su[3] sum[3] 即 2 4
所以我们得出a[2]=3 这个我们动不了 但是窝们有需要 2 所以我们就有了 idx[]数组
我们不动a[2]=3,但我们让 2 改头换面 我们让另一个值代替 2 , 进入新的集合
让 数组a[]发现不了,能够正常运行 让 idx[] 穿插在 a[]中
还是上面的例子
两个集合 {1,2,3} {4}
此时 idx[1]=1, idx[2]=2, idx[3]=3, idx[4]=4
{1,2,3} a[idx[1]]=2,a[idx[2]]=3,a[idx[3]]=3; 大王为 3
{4} a[dx[4]]=4 大王为 4
指令:2 2 4 把 2移到包含4的集合中
idx[2]=5,a[idx[2]]=5 (即a[5]=5); 2改头换面为 5了
此时a[idx[2]]=4 sum[4]=sum[4]+2=6,su[4]=su[4]+1=2
(即a[5]=4,5代表的2) sum[3]=sum[3]-2=4,su[3]=su[3]-1=2
两个集合变为 {1,3} {2,4}
指令:3 1;
idx[1]=1(开始找大王,进入函数) a[1]=1 ,a[2]=3,a[3]=3;
输出 su[3] sum[3] 即 2 4
再来一个指令 3 3
idx[3]=5(开始找大王,进入函数) a[5]=4,a[4]=4;
输出 su[4] sum[4] 即 2 6