Luogu P1196 [NOI2002]銀河英雄傳說:帶權並查集

Leohh發表於2018-02-05

題目連結: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 }

 

相關文章