UVA

来源:互联网 发布:百分百qq综合采集软件 编辑:程序博客网 时间:2024/06/14 01:25

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}
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是联合p和q所在的集合 2是把p在原集合里删除 放入q所在的集合中 3是查询p所在的集合含有的数字个数和总和


思路:

①1的操作是并查集的基础操作 3可以通过集合联结时维护两个数组实现

2的操作有点麻烦 建立了一个id数组 表示一个映射关系 可以将某个数字映射成一个新的数字 这样我们在删除一个集合中的数时 可以先把这个集合中该数的信息都删除 然后建了一个新的点(下标从n+1开始)将原来的点映射成这个新的点 这样再找原来的点的时候 就会通过id数组找到这个新建立的点


#include <iostream>#include <cstdio>#include <cstring>#define max 200010#define ll long longusing namespace std;int pre[max],cnt[max],id[max];ll sum[max];int n;int find(int x){return pre[x]==x?x:pre[x]=find(pre[x]);}int change(int x){int fx=find(id[x]);cnt[fx]-=1;sum[fx]-=(ll)x;id[x]=++n;cnt[id[x]]=1;sum[id[x]]=(ll)x;pre[id[x]]=id[x];}void join(int x,int y){x=find(id[x]);y=find(id[y]);if(x!=y){pre[y]=x;sum[x]+=sum[y];cnt[x]+=cnt[y];}}int main(){int m,i;while(scanf("%d%d",&n,&m)!=EOF){for(i=1;i<=n;i++){pre[i]=id[i]=i;sum[i]=(ll)i;cnt[i]=1;}while(m--){int t,a,b;scanf("%d",&t);if(t==1){scanf("%d%d",&a,&b);join(a,b);}else if(t==2){scanf("%d%d",&a,&b);if(find(id[a])!=find(id[b])){change(a);join(a,b);}}else if(t==3){scanf("%d",&a);printf("%d %lld\n",cnt[find(id[a])],sum[find(id[a])]);}}}return 0;}

② 上面的这种思路有些麻烦难懂,我们仔细想想,其实并查集删除元素的时候,只要这个元素不是根节点,那么操作就很容易了。所以想到给每个节点都先弄一个根节点,这样在操作过程中就可以和普通的操作一样了。

#include <iostream>#include <cstdio>#include <algorithm>#include <cstring>#include <queue>#include <stack>#include <map>#include <cmath>#include <vector>#define max_ 200010#define inf 0x3f3f3f3f#define ll long longusing namespace std;int n,m;int pre[max_],num[max_],sum[max_];int find(int x){    return pre[x]==x?x:pre[x]=find(pre[x]);}void join(int x,int y){    int fx=find(x);    int fy=find(y);    if(fx!=fy)    {        num[fx]+=num[fy];        sum[fx]+=sum[fy];        pre[fy]=fx;    }}void move(int x,int y){    int fx=find(x);    int fy=find(y);    num[fx]--;    sum[fx]-=x;    num[fy]++;    sum[fy]+=x;    pre[x]=fy;}int main(int argc, char const *argv[]) {    while(scanf("%d%d",&n,&m)!=EOF)    {        int i;        for(i=1;i<=n;i++)        {            pre[i]=i+n;            pre[i+n]=i+n;            num[i+n]=1;            sum[i+n]=i;        }        while(m--)        {            int t,x,y;            cin>>t;            if(t==1)            {                cin>>x>>y;                join(x,y);            }            else if(t==2)            {                cin>>x>>y;                if(find(x)!=find(y))                {                    move(x,y);                }            }            else if(t==3)            {                cin>>x;                printf("%d %d\n",num[find(x)],sum[find(x)] );            }        }    }    return 0;}