题意:
有N个珠子,第i个珠子初始放在第i个城市。有两种操作: T A B:把A珠子所在城市的所有珠子放到B城市。 Q A:输出A珠子所在城市编号,该城市有多少个珠子,该珠子转移了多少回。
题解:
在基本并查集的基础上需要两个数组,cnt[i]记录第i个珠子所在城市总共有多少个珠子,tans[i]记录第i个珠子转移了多少回。我开始想的是有T操作是我就把tans[A]++。其实这个是存在问题的,如果A B在同一个城市,其实是不需要转移的。后来我又不怎么明白,为什么要在Find()里面更新tans[]。后来想想,有点明白了。拿样例举例: N=3 1 2 3 。当1的父节点连接2时,tans[1]=1,之后再合并 1,3时,是先找1的父节点2,2直接连到3上,此时是把tans[2]++,因此当查询1所在城市有多少个珠子时,需要tans[1]加上它父亲节点的tans[]。这是一个递归的过程。
1 #include<iostream> 2 #include<cstdio> 3 using namespace std; 4 const int maxn=1e4+50; 5 int f[maxn],cnt[maxn],tans[maxn]; 6 7 void Init(int n) 8 { 9 for(int i=1;i<=n;i++){ 10 f[i]=i;cnt[i]=1;tans[i]=0;//初始时第i个珠子在第i个城市 11 } 12 } 13 14 int Find(int x) 15 { 16 if(x==f[x]) return x; 17 int t=f[x]; 18 f[x]=Find(f[x]); 19 tans[x]+=tans[t]; 20 return f[x]; 21 } 22 23 void Union(int a,int b) 24 { 25 int x=Find(a),y=Find(b); 26 if(x==y) return; 27 f[x]=y; 28 cnt[y]+=cnt[x]; 29 tans[x]++; 30 } 31 32 int main() 33 { 34 int T,N,Q,a,b; 35 char ch; 36 scanf("%d",&T); 37 for(int cas=1;cas<=T;cas++) 38 { 39 printf("Case %d: ",cas); 40 scanf("%d%d",&N,&Q); 41 Init(N); 42 while(Q--) 43 { 44 scanf(" %c",&ch); 45 if(ch=='T'){ 46 scanf("%d%d",&a,&b); 47 Union(a,b); 48 }else{ 49 scanf("%d",&a); 50 int t=Find(a); 51 printf("%d %d %d ",t,cnt[t],tans[a]);//cnt[]输出的根节点的 52 } 53 } 54 } 55 return 0; 56 }