并查集

来源:互联网 发布:华三交换机mac地址绑定 编辑:程序博客网 时间:2024/06/06 21:43

NYOJ431

样例输入
2
3 3
T 1 2
T 3 2
Q 2
3 4
T 1 2
Q 1
T 1 3
Q 1
样例输出
Case 1:
2 3 0
Case 2:
2 2 1
3 3 2

题意:起初球i是被放在i号城市的,在年代更迭,世事变迁的情况下,球被转移了,而且转移的时候,连带该城市的所有球都被移动了:T A B(A球所在的城市的所有球都被移动到了B球所在的城市),Q A(问:A球在那城市?A球所在城市有多少个球呢?A球被转移了多少次呢?)。

本题主要是用到并查集。 难点为, 怎么保存转移次数。 考虑并查集的路径压缩,就是通过路径压缩来更新转移的次数,比如每次移动时,我只需要把这个城市的根结点的转移次数+1,等到以后路径压缩时,子结点自己移动的次数加上根结点移动的次数,就是这个结点总共的移动次数。我们可以用一个tran表示此节点距离它自己现在的祖先(f数组)的转移次数,有点难理解,也就是说如图所示

#include <cstdio>#include <iostream>#include <cstdlib>#include <algorithm>#include <cstring>#include <string>using namespace std;const int maxn=100100;int f[maxn],tran[maxn],sum[maxn];int af,bf,a,b,T,n,m;char s[2];void close(){    fclose(stdin);    fclose(stdout);    exit(0);}int getfather(int k){    if (k==f[k])        return k;    return getfather(f[k]);}int find(int k){    if (f[k]==k)        return k;    int t=f[k];    f[k]=find(f[k]);    if (t!=f[t])        tran[k]+=tran[t];    return f[k];}void work(){}void init (){freopen("dragon.in","r",stdin);freopen("dragon.out","w",stdout);    scanf("%d",&T);     int cnt=0;     while (T--)     {         cnt++;         printf("Case %d:\n",cnt);         memset(tran,0,sizeof(tran));         memset(f,0,sizeof(f));         scanf("%d %d",&n,&m);         for (int i=1;i<=n;i++)         {             f[i]=i;             sum[i]=1;         }         for (int i=1;i<=m;i++)         {             scanf("%s",s);             if (s[0]=='T')             {                 scanf("%d %d",&a,&b);                 af=getfather(a);                 bf=getfather(b);                 f[af]=bf;                 tran[af]++;                 sum[bf]+=sum[af];             }             else             {                 scanf("%d",&a);                 af=find(a);                 printf("%d %d %d\n",af,sum[af],tran[a]);             }         }     }}int main (){    init();    work();    close();    return 0;}
#include<stdio.h>int t,a,b,n,q,cnt[10005];char s[5];struct C{    int pre,num;}fa[10005];void In(){    scanf("%d %d%*c",&n,&q);    for(int i=1;i<=n;i++) {        fa[i].pre=i;        fa[i].num=1;        cnt[i]=0; //每个点移动次数一开始都是0    }}int Find(int x){    if(x!=fa[x].pre) {        int tmp=fa[x].pre;        fa[x].pre=Find(fa[x].pre);        cnt[x]+=cnt[tmp]; // 加上它父亲的移动次数。    }    return fa[x].pre;}int main(){    scanf("%d%*c",&t);    for(int ca=1;ca<=t;ca++) {        In();        printf("Case %d:\n",ca);        for(int k=1;k<=q;k++) {            scanf("%s ",s);            if(s[0]=='T') {                int fx,fy;                scanf("%d %d%*c",&a,&b);                fx=Find(a);                fy=Find(b);                if(fx!=fy) {                    fa[fy].num+=fa[fx].num;                    fa[fx].num=0;                    fa[fx].pre=fy;                    cnt[fx]=1; // 祖先节点只会移动一次。                }            } else {                scanf("%d%*c",&a);                int fx=Find(a);                printf("%d %d %d\n",fx,fa[fx].num,cnt[a]);            }        }    }    return 0;}

0 0
原创粉丝点击