1. 程式人生 > >【資料結構並查集】POJ1988——線樹上的帶權並查集

【資料結構並查集】POJ1988——線樹上的帶權並查集

問題描述:

給定30000個方塊,一開始每個方塊各自一摞,每次有兩種操作的方法,一種是將含有編號x的一摞放在含有編號y的一摞上,另一種是統計編號x的方塊下有幾個方塊,每次將第二種操作的結果輸出。

求解方法:

(本解法由網上其他博主學來) 我們將落在最上面的方塊作為線樹的根節點,並建立以下三個陣列: (1)par[i]陣列:編號i的方塊的父節點編號。 (2)son[i]陣列:編號i的方塊的子節點個數(只需統計作為根節點的,包含自身)。 (3)deep[i]陣列:線上樹上編號i方塊距離根結點的距離。 在此基礎上,我們對並查集本身的兩個操作(查詢根節點並更新所有查詢結點的父節點為根節點、合併)進行闡述: 合併

:首先找到二者的根節點,在根節點不相等的前提下,首先將後者的根節點的父節點改為前者的根節點,然後將其deep改為前者根節點的子節點數,最後將前者根節點的子節點數加上後者的子節點數。 查詢:如果是根節點則返回,否則首先記錄父節點編號,然後遞迴呼叫並路徑壓縮,再將deep加上原父節點編號的deep,最後返回父節點編號。 最終的結果為查詢根節點後用他的孩子數減去與根節點的距離再減1得到。

注意點:

輸入字元後決定輸入幾個數可以用scanf(“%*c%c”,&c)解決。

AC程式碼如下:

#include<iostream>
#include<cstdio>
using namespace std; int son[30000+5]; int par[30000+5]; int deep[30000+5]; void init() { for(int i=1;i<=30000;i++) { par[i]=i; deep[i]=0; son[i]=1;//孩子節點的總個數(包含自身) } } int find(int x) { if(par[x]==x) return x; int temp=par[x]; par[x]=find(par[x]); deep[x]+=deep[temp]; return
par[x]; } int main() { char c; int p; scanf("%d",&p); init(); while(p--) { scanf("%*c%c",&c); if(c=='M') { int x,y; scanf("%d%d",&x,&y); x=find(x); y=find(y); if(x!=y) { par[y]=x; deep[y]+=son[x]; son[x]+=son[y]; } } else { int x; scanf("%d",&x); int query=find(x); printf("%d\n",son[query]-deep[x]-1); } } return 0; }