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}
Rujia Liu's Present 3: A Data Structure Contest Celebrating the 100th Anniversary of Tsinghua UniversitySpecial Thanks: Yiming Li
Note: Please make sure to test your program with the gift I/O files before submitting!
______________________________________________________________________________________________________________
操作1,3实现比较容易,主要是操作2:
这题之后对并查集有了新的认识,可以把pre数组看成一组盒子,它的下标是盒子的编号,然后adr数组用来存放盒子的编号,它的下标是1~n这些数字,表示把i放进第adr[i]的盒子里,pre对盒子进行合并,查找。而删除操作则是把盒子里的数取出来放在n以后的盒子里,让这个盒子独立成为一个集合,2操作还要把这个集合合并到q的集合中。还要注意时刻维护sum数组和cnt数组。
#include<cstdio>
#include<cstring>
using namespace std;
const int M = 1e5 +5;
int pre[2*M];
int cnt[2*M];
int sum[2*M];
int adr[M];
int tail;
int n;
int Find(int x)
{
int r=x;
while(r!=pre[r])
r=pre[r];
return r;
}
void join(int x, int y)
{
int tx,ty;
tx=Find(x);
ty=Find(y);
if(tx!=ty)
{
pre[tx] = ty;
cnt[ty] += cnt[tx];
sum[ty] += sum[tx];
}
}
void op1()
{
int q,p;
scanf("%d%d", &p, &q);
join(adr[p],adr[q]);
}
void op2()
{
int p,q;
scanf("%d%d", &p, &q);
if(p!=q)//这个地方很重要!
{
int pr = Find(adr[p]);
cnt[pr]--;
sum[pr] -= p;
adr[p] = ++tail;
pre[adr[p]] = adr[p];
sum[adr[p]] = p;
cnt[adr[p]] = 1;
join(adr[p],adr[q]);
}
}
void op3()
{
int p,r;
scanf("%d", &p);
r = Find(adr[p]);
printf("%d %d\n", cnt[r], sum[r]);
}
int main()
{
int m,cmd;
while(~scanf("%d%d", &n, &m))
{
tail = n;
for(int i=1;i<=n;i++)
{
adr[i] = i;
pre[adr[i]] = i;
sum[adr[i]] = i;
cnt[adr[i]] = 1;
}
for(int i=1;i<=m;i++)
{
scanf("%d", &cmd);
if(cmd==1)
op1();
else if(cmd==2)
op2();
else if(cmd==3)
op3();
}
}
return 0;
}