题目:
Almost Union-Find
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). The size of input file does not exceed 5MB.
Output
For each type-3 command, output 2 integers: the number of elements and the sum of elements.
Sample Input
5 7
1 1 2
2 3 4
1 3 5
3 4
2 4 1
3 4
3 3
Output for the Sample Input
3 12
3 7
2 8
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个节点,有三种操作,
1 :将两个节点所在集合合并;
2:将一个元素从一个集合移动到另一个集合;
3:查询指定元素所在集合中的元素个数,和权值总和;
思路:
开一个sum数组和num数组,分别记录集合中的权值总和,和元素个数,
合并的时候利用并查集直接合并,但是操作二移动的时候就会出现问题,
如果移动的是集合中的根节点时,直接处理会把整个集合移动到另一个集合中去,
所以要再开一个数组 id 记录每个节点的真实下标,每次要移动时,
先消除这个元素在原集合中的影响,再新开一个点来表示这个元素,再把这个点
与集合合并。
代码:
#include<bits/stdc++.h>
#define ll long long
#define N 1008611
#define inf 0x3f3f3f3f
using namespace std;
int f[200010],sum[200010],num[200010],id[200010];
int getf(int x)
{
if(x==f[x])
return x;
return f[x]=getf(f[x]);
}
void Inint()
{
for(int i=0; i<100010; i++)
{
f[i]=i;
sum[i]=i;
id[i]=i;
num[i]=1;
}
}
int main()
{
int n,m;
while(~scanf("%d%d",&n,&m))
{
int nn=n+1;
Inint();
for(int i=0; i<m; i++)
{
int x;
scanf("%d",&x);
if(x==1)//合并集合
{
int p,q;
scanf("%d%d",&p,&q);
int t1=id[p];//找到p q 真实的表示
int t2=id[q];
t1=getf(t1);
t2=getf(t2);
if(t1==t2)
continue;
f[t2]=t1;
sum[t1]+=sum[t2];
num[t1]+=num[t2];
}
else if(x==2)//将元素从一个集合移动到另一个集合中
{
int p,q;
scanf("%d%d",&p,&q);
int t1=id[p];
int t2=id[q];
t1=getf(t1);
t2=getf(t2);
if(t1==t2)
continue;
sum[t1]-=p;//消除 p 在原来集合中的影响
num[t1]-=1;
id[p]=nn++;//新建一个节点来表示 p
t1=id[p];
f[t1]=t2;
sum[t2]+=p;//移动到另一个集合
num[t2]+=1;
}
else if(x==3)
{
int y;
scanf("%d",&y);
y=getf(id[y]);
printf("%d %d\n",num[y],sum[y]);
}
}
}
return 0;
}