这题的关键是如何统计转移的次数
根结点都是最多移动一次的,所以记录移动次数时把自己的加上父亲结点的就是移动总数了
这里要注意:更新移动次数时,一定要先更新父亲的,在更新自己的,即用递归从最顶层开始往下更新
我的方法是在调用find_root前先更新一下,再路径压缩。
网上看了有人写的,在查找父节点的时候同时更新的代码:
int find_root(int x){ int fa; if(father[x]==x) return x; fa=find(father[x]); int tmp=father[x]; trans[x]+=trans[tmp]; father[x]=fa; return fa; } /* trans表示的是从x转移到father[x]用的步数 我们更新要变成x转移到根节点,这里也就是s用的步数 就是要father[x]的trans更新完才能更新x的trans */
下面附上我的代码:
#include <iostream> #include <stdio.h> #include <string.h> #include <algorithm> #include <vector> #include <set> using namespace std; int father[10010]; int ranks[10010]; //表示以i为根节点的集合中所含有的个数 int trans[10010]; //trans[i]表示i所移动的次数 int n,q; void init(){ for(int i=0;i<=n;i++){ father[i]=i; ranks[i]=1; } } //x的父节点更新完相应的trans后,才能更新x的trans int update(int x){ if(father[x]==x) return trans[x]; else{ trans[x]+=update(father[x]); return trans[x]; } } int find_root(int x){ /*一开始更新trans的方法写在这里,结果WA。 后来发现: 如果写在这里,在第一次find_root时就能更新完全, 但是接下来还会调用find_root方法,父节点又会再更新一遍,等于多加了几次 改了一下,每次在调用find_root方法前,调用update()方法,AC */ //trans[x]+=update(father[x]); if(father[x]!=x){ father[x]=find_root(father[x]); } return father[x]; } void Union(int a,int b){ trans[a]+=update(father[a]); trans[b]+=update(father[b]); int x=find_root(a); int y=find_root(b); if(x==y) return; //是trans[x]++,一开始写了trans[a]++。。。 trans[x]++; father[x]=y; ranks[y]+=ranks[x]; } int main() { int t,a,b,x,y,z; char ch[4]; scanf("%d",&t); for(int i=1;i<=t;i++){ memset(trans,0,sizeof(trans)); printf("Case %d: ",i); scanf("%d%d",&n,&q); init(); for(int i=1;i<=q;i++){ scanf("%s",ch); if(ch[0]=='T'){ scanf("%d%d",&a,&b); Union(a,b); } else{ scanf("%d",&a); trans[a]+=update(father[a]); x=find_root(a); y=ranks[x]; z=trans[a]; printf("%d %d %d ",x,y,z); } } } return 0; }