并查集

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;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值