Problem A
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 University
Special Thanks: Yiming Li
Note: Please make sure to test your program with the gift I/O files before submitting!
本题的难点就在2操作:
要把一个元素从一个集合移到另一个集合。
我们知道并查集是不能删除元素的,因为整个结构是单向的,不知道儿子是什么
但这里移动元素,实际上就蕴含着删除的操作。那该怎么办呢?
有一个想法可能会很快想到,并不实际删除要移动的元素p,即不改变p集合原来的路径,但集合的权值还是要改变(相当于把p看成一个虚点),然后把p直接“挂到”q的集合中去。
注意,这里一定要保证p在以后永远只能作为一个叶子,不能再做父亲,否则会覆盖掉最初(作为父亲时)的路径,并且可以证明只做叶子的做法一定存在。
这样在写程序时要注意的就有以下几点:
1.让每个元素的值有二维状态,一维用于保存,这个元素在最初集合时的情况,另一维保存这个元素现在实际的情况
2.在合并时注意权值的改变,应该用哪一维状态。
3.在find()时先判断这个元素是否真的存在过去和现在两个状态,从而选择搜索的路径不同,并且在写路径压缩时也有些不同。
第一道加权并查集题。。。
92
93
94
95
#include <iostream>
#include <cstdio>
using namespace std;
const int Max = 1e5+5;
long long sum[Max];
int a[Max];
int cent[Max];
int id[Max];
int deep;
void init(int n)
{
for(int i = 1; i <= n; i++)
{
a[i] = i;
cent[i] = 1;
sum[i] = i;
id[i] = i;
}
deep = n;
}
int find(int x)
{
return a[x] == x ? x :( a[x] = find(a[x]) );
}
void Union(int x, int y)
{
int fx = find(x);
int fy = find(y);
sum[fy] += sum[fx];
cent[fy] += cent[fx];
a[fx] = fy;
}
void chang(int x)
{
int fx = find(id[x]);
id[x] = ++deep;
sum[fx] -= x;
cent[fx] --;
sum[id[x]] = x;
cent[id[x]] = 1;
a[id[x]] =id[x];
}
int main()
{
int n,m,type,p,q;
#ifdef xxz
freopen("in","r",stdin);
#endif // xxz
while(scanf("%d%d",&n,&m) != EOF)
{
init(n);
for(int i = 0; i <m; i++)
{
scanf("%d",&type);
if(type == 1)
{
scanf("%d%d",&p,&q);
if(find(id[p]) != find(id[q]))
Union(id[p],id[q]);
}
else if(type == 2)
{
scanf("%d%d",&p,&q);
if(find(id[p]) != find(id[q]))
{
chang(p);
Union(id[p],id[q]);
}
}
else
{
scanf("%d",&p);
int fp = find(id[p]);
printf("%d %lld\n",cent[fp] , sum[fp] );
}
}
}
return 0;
}