1. 程式人生 > >poj1703 並查集,維護不同集合

poj1703 並查集,維護不同集合

題目大意:

警方決定搗毀兩大犯罪團伙:龍幫和蛇幫,顯然一個幫派至少有一人。該城有N個罪犯,編號從1至N(N<=100000。將有M(M<=100000)次操作。
D a b 表示a、b是不同幫派
A a b 詢問a、b關係

對於每一個A操作,回答"In the same gang."或"In different gangs." 或"Not sure yet."

(大意摘自vjudge)

題解:維護不同集合關係的經典題目,和poj食物鏈那道題一樣

可參考https://blog.csdn.net/freezhanacmore/article/details/8774033

直接上程式碼

 1
#include<cstdio> 2 #include<cstring> 3 #include<iostream> 4 using namespace std; 5 const int maxn=1e5+100; 6 int head=0,tail=10002; 7 int n,m; 8 int father[maxn*2],rank[maxn*2],vis[maxn*2]; 9 int findf(int x) 10 { 11 int k, j, r; 12 r = x; 13 while(r != father[r]) 14 r = father[r];
15 k = x; 16 while(k != r) 17 { 18 j = father[k]; 19 father[k] = r; 20 k = j; 21 } 22 return r; 23 } 24 25 void merg(int x,int y) 26 { 27 int fx=findf(x); 28 int fy=findf(y); 29 if(rank[fx]<=rank[fy]) 30 { 31 father[fy]=fx; 32 rank[fx]++;
33 } 34 else 35 { 36 father[fx]=fy; 37 rank[fy]++; 38 } 39 } 40 void ini() 41 { 42 for(int i=0;i<=2*maxn;i++) father[i]=i; 43 father[tail]=tail; 44 memset(rank,0,sizeof(rank)); 45 } 46 void solve() 47 { 48 scanf("%d%d",&n,&m); 49 ini(); 50 for(int i=1;i<=m;i++) 51 { 52 char a;int b,c; 53 cin>>a;scanf("%d%d",&b,&c); 54 if(a=='A') 55 { 56 //for(int i=1;i<=n;i++) printf("%d ",father[i]);puts(""); 57 if(findf(b)==findf(c)||findf(b+n)==findf(c+n)) 58 puts("In the same gang."); 59 else if(findf(b)==findf(c+n)||findf(c)==findf(b+n)) 60 puts("In different gangs."); 61 else puts("Not sure yet."); 62 } 63 else 64 { 65 merg(b,c+n); 66 merg(c,b+n); 67 } 68 } 69 } 70 71 int main() 72 { 73 //freopen("in.txt","r",stdin); 74 int T; 75 cin>>T; 76 for(int i=1;i<=T;i++) solve(); 77 }
View Code

 

 

 

思路:除了像普通的並查集定義一個 p[] 記錄父親節點外,還定義一個 r[] 記錄當前點與其所屬的連通分量的根節點的關係。            r[] = 0 表示屬於同一個幫派; r[] = 1表示與其根節點屬於不同的幫派。                       開始時初始化自己是自己的父親 p[x] = x,自己與自己屬於同一類 r[x] = 0.           一旦輸入 D 斷定 x 和 y 屬於不同集合後,就連線 x 和 y 所在的樹,同時更新 r[]           一旦輸入 A           如果 find(x) 不等於 find(y) 說明還沒有判斷過 x 與 y 直接輸出關係不確定即可      Not sure yet.      如果find(x)等於 find(y) ,但是他們的r不等,說明屬於不同幫派,輸出In different gangs.                               如果他們的r相等,說明屬於同一個幫派,則輸出In the same gang注意:1.find()函式尋找根節點的時候要不斷的更新 r         根據子節點與父親節點的關係和父節點與爺爺節點的關係,推導子節點與爺爺節點的關係       如果 a 和 b 的關係是 r1, b 和 c 的關係是 r2,       那麼 a 和 c 的關係就是 (r1+r2)%2 . PS:因為只用兩種情況所以對 2 取模。       如果實在不好理解,那麼我們就列舉推理一下,共有 2*2 = 4種情況:       (a, b) (b, c)  (a, c)  (r1+r2)%2          0 0       0        0        a 和 b是同類 , b 和 c 是同類, 所以 a 和 c 也是同類          0      1       1        1        a 和 b是同類 , b 和 c 是異類, 所以 a 和 c 也是異類          1      0       1        1        a 和 b是異類 , b 和 c 是同類, 所以 a 和 c 是異類          1      1       0        0        a 和 b是異類 , b 和 c 是異類, 所以 a 和 c 是同類     2.Union()聯合兩棵樹的時候也要更新兩棵樹的根的關係       定義:fx 為 x的根節點, fy 為 y 的根節點       聯合時,使得 p[fx] = fy; 同時也要尋找 fx 與 fy 的關係。關係為:(r[x]+r[y]+1)%2       如何證明?       fx 與 x 的關係是 r[x],        x 與 y 的關係是 1 (因為確定是不同類,才聯合的),        y 與 fy 關係是 r[y],模 2 是因為只有兩種關係       所以又上面的一點所推出的定理可以證明 fx 與 fy 的關係是: (r[x]+r[y]+1)%2--------------------- 作者:cfreezhan 來源:CSDN 原文:https://blog.csdn.net/freezhanacmore/article/details/8774033 版權宣告:本文為博主原創文章,轉載請附上博文連結!