1. 程式人生 > >HDU 3635 Dragon Balls (並查集路徑壓縮)

HDU 3635 Dragon Balls (並查集路徑壓縮)

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.

Input

The first line of the input is a single positive integer T(0 < T <= 100). 
For each case, the first line contains two integers: N and Q (2 < N <= 10000 , 2 < Q <= 10000). 
Each of the following Q lines contains either a fact or a question as the follow format: 
  T A B : All the dragon balls which are in the same city with A have been transported to the city the Bth ball in. You can assume that the two cities are different. 
  Q A : WuKong want to know X (the id of the city Ath ball is in), Y (the count of balls in Xth city) and Z (the tranporting times of the Ath ball). (1 <= A, B <= N)

Output

For each test case, output the test case number formated as sample output. Then for each query, output a line with three integers X Y Z saparated by a blank space.

Sample Input

2
3 3
T 1 2
T 3 2
Q 2
3 4
T 1 2
Q 1
T 1 3
Q 1

Sample Output

Case 1:
2 3 0
Case 2:
2 2 1
3 3 2

題目大意:

初始時,有n個龍珠,編號從1到n,分別對應的放在編號從1到n的城市中。

現在又2種操作:

T A B,表示把A球所在城市全部的龍珠全部轉移到B城市。(第一次時,因為A球所在的城市只有一個球,所以只移動1個,如果有多個,則全部移動)。

Q A,表示查詢A。要求得到的資訊分別是:A現在所在的城市,A所在城市的龍珠數目,A轉移到該城市移動的次數(如果沒有移動就輸出0)

解題思路:

每次移動時,我只需要把這個城市的根結點的轉移次數+1,等到以後路徑壓縮時,子結點自己移動的次數加上根結點移動的次數,就是這個結點總共的移動次數,這個是在find函式裡面實現的,總體還是城市是固定的,每個子節點的移動次數都儲存在它最開始所待的城市裡面,在呼叫find函式時,它會加上它的根節點的移動次數,這樣就是它的總的移動次數。

遞迴路徑壓縮原理:

比如有4層,根結點為1,然後第二層2,第三層3,第四層4.那麼,第一層路徑壓縮過程中進入第二層,然後第三層,然後第四層,第四層也就是根,找到根結點,返回第三層,第三層+上第四層的轉移次數,然後返回第二層,第二層轉移次數加上第三層轉移次數,返回第一層,第一層轉移次數加上第二層轉移次數,同時,在這個過程中也把下面3層的結點的父節點直接更新為和根結點,也就是1相連,查詢結束後,稱為2層結構,根結點1在一層,234都處於第二層

程式碼:

/*
本題必須用scanf和printf,不然會超時 
*/
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <string.h>
using namespace std;
struct city
{
	int parent;//父節點,當前這個點指向的點 
	int son;//子節點,有多少點指向當前點(包括自己) 
	int t;//移動次數 
}f[10005];
int find(int x)
{
	if(x==f[x].parent)
	return x;
	else
	{
		int temp=f[x].parent;
		f[x].parent=find(f[x].parent);//路徑壓縮的遞迴實現 
		f[x].t+=f[temp].t; 
		return f[x].parent;
	}
}
void join(int x,int y)
{
	int root1=find(x);
	int root2=find(y);
	if(root1!=root2)
	{
		f[root1].parent=root2;
		f[root1].t++;
		f[root2].son+=f[root1].son;//合併 
		f[root1].son=0;
	}
}
int main()
{
	char s;
	int n,m,i,j,k,root;
	int t;
	int c=1;
	scanf("%d",&t);
	while(t--)
	{
		scanf("%d %d",&n,&m);
		for(i=1;i<=n;i++)
		{
			f[i].parent=i;
			f[i].son=1;
			f[i].t=0;
		}
		printf("Case %d:\n", c++);
		while(m--)
		{
			cin>>s;
			if(s=='T')
			{
				int x,y;
				scanf("%d %d",&x,&y);
				join(x,y);
			}
			else
			{
				
				int q;
				scanf("%d",&q);
				root=find(q);//這裡很關鍵,在這裡呼叫find函式,會有一個子節點的移動次數+父節點移動次數的操作 
				printf("%d %d %d\n",root,f[root].son,f[q].t);
			}
		}
	}
	return 0;
}