題目大意
開始有N堆磚塊,編號為1,2....N,每堆都只有一個。之後可以進行兩種操作:
(1)M X Y 將編號為X的磚塊所在的那堆磚拿起來放到編號為Y的磚塊所在的堆上;
(2)C X 查詢編號為X的磚塊所在的堆中,在磚塊X下方的所有磚塊的數目
題目分析
典型的集合合併和查詢,因此採用並查集。並查集的基本框架就是一個GetPar函式(實現查詢集合的祖先,同時實現路徑壓縮),一個Union函式(實現將兩個集合合併),一個SameGroup函式(判斷兩個元素是否屬於同一個集合)。
在利用並查集解決具體問題的時候,需要做的是設定一個數據結構用於存放問題所需要的資訊,然後在GetPar函式和Union函式中更新這個資料結構。
在本題中,維護資訊 SumOfStack(每堆中的所有磚塊數目),NumOfUnderBlock(堆中在磚塊下方的磚塊數目)。將每堆磚塊集合的編號(即集合的根)設定為該堆最下方的磚塊號,則在合併的時候,上堆的根節點的父節點設定為下堆的根節點,可以更新的資料為上堆的根節點下方的磚塊數目
和下堆的根節點代表的堆總磚塊數
。
這樣,每堆的根節點的SumOfStack資訊是正確的,而每次合併後上堆的原根節點的NumOfUnderBlock資訊也是正確的;對於某個磚塊b,在GetPar的過程中,由於b的gPar[b]可能沒被更新,如果沒被更新,則b的NumOfUnderBlock[b]表示在b之前所在的堆中位於b下方的磚塊數目
也是正確的,於是將此時的NumOfUnderBlock[b] + NumOfUnderBlock[gPar[b]](注意,這裡的gPar[b]為b原來的堆中的根),即可得到最終的NumOfUnderBlock[b],這可以在GetPar函式的遞迴中完成。
實現(c++)
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#define MAX_STACK_NUM 30001
int gPar[MAX_STACK_NUM];
int gSumOfStack[MAX_STACK_NUM];
int gNumOfUnderBlock[MAX_STACK_NUM]; int GetPar(int c){
if (c != gPar[c]){
int p = gPar[c];
gPar[c] = GetPar(gPar[c]); //資訊維護
gNumOfUnderBlock[c] += gNumOfUnderBlock[p];
}
return gPar[c];
} void Union(int a, int b){
int p1 = GetPar(a);
int p2 = GetPar(b);
gPar[p1] = p2; //資訊維護
gNumOfUnderBlock[p1] = gSumOfStack[p2];
gSumOfStack[p2] += gSumOfStack[p1];
} void Init(){
for (int i = 0; i < MAX_STACK_NUM; i++){
gSumOfStack[i] = 1;
gNumOfUnderBlock[i] = 0;
gPar[i] = i;
}
}
int main(){
int p, X, Y;
scanf("%d", &p);
char op;
Init();
for (int i = 0; i < p; i++){
getchar();
scanf("%c", &op);
if (op == 'M'){
scanf("%d %d", &X, &Y);
Union(X, Y);
}
else if (op == 'C'){
scanf("%d", &X);
GetPar(X);
printf("%d\n", gNumOfUnderBlock[X]);
}
}
return 0;
}