題目地址:
http://acm.hdu.edu.cn/showproblem.php?pid=3635
題目類型: 並查集
題目:
Five hundred years later, the number of dragon balls will increase unexpectedly, so it's too difficult for Monkey King(WuKong) to gather all of the dragon balls together.
His country has N cities and there are exactly N dragon balls in the world. At first, for the ith dragon ball, the sacred dragon will puts it in the ith city. Through long years, some
cities' dragon ball(s) would be transported to other cities. To save physical strength WuKong plans to take Flying Nimbus Cloud, a magical flying cloud to gather dragon balls.
Every time WuKong will collect the information of one dragon ball, he will ask you the information of that ball. You must tell him which city the ball is located and how many dragon
balls are there in that city, you also need to tell him how many times the ball has been transported so far.
範例輸入:
23 3T 1 2T 3 2Q 23 4T 1 2Q 1T 1 3Q 1
範例輸出:
Case 1:2 3 0Case 2:2 2 13 3 2
題目大意:
有N的龍珠,編號為1~N,開始時分別分布在編號為1~N的城市。
然後如果是輸入T a b,那麼進行移動, 把編號a的龍珠所在的城市的所有龍珠移動到編號b龍珠所在的城市。
輸入Q a, 那麼輸出a龍珠所在的城市, a龍珠所在城市有的龍珠的個數, 以及a龍珠移動的次數。
思路與總結:
首先,感覺這題確實是一道很好的並查集題目,可以加深對並查集和壓縮路徑的理解。
做這題有點略糾結,WA了很多次,說明我對並查集的理解還是不夠。
求一個龍珠在那個城市很好辦, 只需要find(x),找到x的根結點,即是x所在的城市
求一個城市有多少個龍珠也很好辦, 只需要加上一個rank數組,用來表示並查集的秩,及樹的高度,就是那個城市的龍珠的數量。
而求一個龍珠移動的次數,比較難搞,也正式我糾結之處。
首先,當一座城市的龍珠全部移動到另一個城市之後,那麼這個城市的龍珠數量變為0, 也就是說沒有龍珠是在這個城市了,那麼之後都不會再有龍珠移動到
這個空城市。因為T a x,必須是移動到龍珠x所在的城市,而現在已經沒有龍珠落在空城市了,對於任意龍珠x,都不會是落在空城市。最後可以得出結論,一個
城市只能移動一次。
開一個數組num表示各個求的移動次數。
每次移動時, 設是T a b, 那麼b所在城市(即b的根結點)的最初始的球, 是這個球的第一次移動, 這時給初始球的移動次數置為1。
光光是這樣做的話,還不夠。因為這樣只增加了根結點的初始那個球的次數,還有其他球在這個跟結點的次數還沒有加。
別急, 這個這個步驟可以留給路徑壓縮的時候做。
還沒進行路徑壓縮時,它的父親是以前的那個跟結點,而這時候,之前欠下來的債也還了,讓它的移動次數加上它所有祖宗的移動次數(這個是關鍵,也比較
難理解)。
最後,它只想了新結點。 次數也跟新了。
進行路徑壓縮之後,每個球的父親結點就是它所在的城市。
#include<iostream>#include<cstdio>#include<cstring>#define MAXN 10002using namespace std;int N, Q, father[MAXN], rank[MAXN], num[MAXN];void init(){ for(int i=1; i<=N; ++i){ father[i] = i, rank[i] = 1, num[i]=0; }}int find(int x){ if(x==father[x]) return x; int t=father[x]; father[x] = find(father[x]); num[x] += num[t]; return father[x]; }void Union(int x,int y){ int a=find(x); int b=find(y); if(a!=b){ father[a] = b; rank[b] += rank[a]; num[a] = 1; // 這是球a第一次移動 }}int main(){#ifdef LOCAL freopen("input.txt","r",stdin);#endif int T,a,b,k,cas=1; char cmd[2]; scanf("%d", &T); while(T--){ printf("Case %d:\n",cas++); scanf("%d %d", &N,&Q); init(); for(int i=0; i<Q; ++i){ scanf("%s", cmd); if(cmd[0]=='T'){ scanf("%d %d", &a, &b); Union(a, b); } else{ scanf("%d", &k); int x = find(k); int cnt=0; printf("%d %d %d\n", x, rank[x],num[k]); } } } return 0;}
—— 生命的意義,在於賦予它意義。
原創
http://blog.csdn.net/shuangde800
, By
D_Double