标签:杭电3635 dragon balls 并查集 树

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
题意:有N个球,标号1,2.3…原来分别在各自标号对应的城市1,2,3…后来经过了一些变换后现在让你球一些球的状态:
两个操作:
T A B 表示把A球所在的城市里所有球添加到B球所在的城市
Q A 表示 输出A所在的城市,A所在的城市里的球的个数,A转移的次数
分析:
可以想到是用并查集,前两个问题都很好解决,一个找根节点另一个找这棵树上总的元素个数,难的是第三个问题,怎么求转移的次数呢?
我们可以设一个数组,来记录,每次都要更新,显然如果在合并函数中来计算式很麻烦的,我们采用另一种方法,在合并函数中,我们只让转移的根节点加一,其他不管,在下一次合并之前我们要压缩路径,压缩的过程中,我们来更新转移次数,但是切记我们一定要从上向下更新,这一过程也只能用递归加回溯来实现了!
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int city[10100],per[10100],num[10100];
int m,n,mov[10100];
char s[3];
void init()
{
for(int i=1;i<=m;i++)
{
per[i]=i;
num[i]=1;
mov[i]=0;//初始化数组
}
}
int find(int x)
{
// int t=x;
// while(t!=per[t])
// t=per[t];
// int i=x,j;
// while(i!=t)
// {
// j=per[i];
// per[i]=t;
// i=j;
// }
// return t;//开始的时候配合下面注释掉的部分写的普通压缩路径
if(x!=per[x])
{
int t=per[x];
per[x]=find(t);
mov[x]+=mov[t];//更新时,子节点的移动的次数等于他本身移动的次数加上父节点移动的次数
}
return per[x];
}
void join(int x,int y)//合并两颗不同的数
{
int fx=find(x);
int fy=find(y);
if(fx!=fy)
{
// for(int i=1;i<=m;i++)
// {
// if(find(i)==fx)
// mov[i]++;
// }//这是我开始的想法,在join函数中直接更新,但是果断超时了
per[fx]=fy;
num[fy]+=num[fx];
mov[fx]=1;//每次跟接待你只可能移动一次,所以只让根节点等于1便可
}
}
int main()
{
int N,a,b,cot=1;
scanf("%d",&N);
while(N--)
{
scanf("%d%d",&m,&n);
init();
printf("Case %d:\n",cot++);//控制格式
for(int i=0;i<n;i++)
{
scanf("%s",s);//采用%S输入的好处是不用处理吸收回车符
if(s[0]=='T')//两种不同的操作
{
scanf("%d%d",&a,&b);
join(a,b);
}
else
{
scanf("%d",&a);
int t=find(a);//找到a的根节点
printf("%d %d %d\n",t,num[t],mov[a]);
}
}
}
return 0;
}
版权声明:本文为博主原创文章,未经博主允许不得转载。
标签:杭电3635 dragon balls 并查集 树
原文地址:http://blog.csdn.net/nvliba/article/details/48009265