題目連結:https://www.luogu.org/problemnew/show/P1196
題意:
有30000個戰艦佇列,編號1...30000。
有30000艘戰艦,編號1...30000,初始時第i艘戰艦在第i個戰艦佇列中。
然後t個操作:
(1)M i j:將戰艦i所在的佇列整體接到戰艦j所在佇列的尾部。
(2)C i j:詢問戰艦i,j之間有多少艘戰艦。若i,j不在同一佇列中,輸出-1。
題解:
dis[i]表示戰艦i與par[i]之間的距離。
siz[i]表示戰艦i所在佇列的大小。
find(x):
old為原本的par[x],now為路徑壓縮後的par[x]。
此時關係為:x -> old -> now
所以此時dis[x] = dis(x to old) + dis(old to now)
即:dis[x] += dis[old]
unite(x,y):
px,py分別為x,y的真正祖先。
因為是將px的整個佇列接到了py佇列的後面
所以dis[px]=siz[py], siz[py]+=siz[px]
(因為程式中只會用到隊首的siz值,所以只更新py的siz就行了)
query(x,y):
如果不在同一集合中直接return -1.
先讓x,y找到它們的真正祖先。
然後答案就是abs(dis[x]-dis[y]) - 1
AC Code:
1 #include <iostream> 2 #include <stdio.h> 3 #include <string.h> 4 #define MAX_N 30005 5 6 using namespace std; 7 8 int t; 9 int par[MAX_N]; 10 int dis[MAX_N]; 11 int siz[MAX_N]; 12 13 void init() 14 { 15 for(int i=1;i<=30000;i++) 16 { 17 par[i]=i; 18 dis[i]=0; 19 siz[i]=1; 20 } 21 } 22 23 int find(int x) 24 { 25 if(par[x]!=x) 26 { 27 int old=par[x]; 28 int now=find(par[x]); 29 par[x]=now; 30 dis[x]+=dis[old]; 31 } 32 return par[x]; 33 } 34 35 void unite(int x,int y) 36 { 37 int px=find(x); 38 int py=find(y); 39 if(px==py) return; 40 par[px]=py; 41 dis[px]=siz[py]; 42 siz[py]+=siz[px]; 43 } 44 45 bool same(int x,int y) 46 { 47 return find(x)==find(y); 48 } 49 50 inline int abs(int x) 51 { 52 return x>0 ? x : -x; 53 } 54 55 int query(int i,int j) 56 { 57 if(!same(i,j)) return -1; 58 find(i); find(j); 59 return abs(dis[i]-dis[j])-1; 60 } 61 62 int main() 63 { 64 init(); 65 cin>>t; 66 char opt; 67 int i,j; 68 while(t--) 69 { 70 cin>>opt>>i>>j; 71 if(opt=='M') unite(i,j); 72 else cout<<query(i,j)<<endl; 73 } 74 }