E - A Bug's Life POJ - 2492
D - Find them Catch them
E - 食物鏈 POJ - 1182
種類並查集總歸一個思想,就是把一堆的東西分為一些種類,但實際上,每個東西的種類並不確定,強行給它確定一個種類的會不好處理,因為它本身的不確定性。但是如果把他們歸到一個集合里,有一個共同的根節點,當要判斷某兩個東西的關系時,就可以根據他們跟根節點的關系判斷。就以食物鏈這題舉例。
題目大意就是,三類動物A吃B,B吃C,C吃A這么一個食物鏈,然后是有一堆動物並不確定他們是哪類動物,有個人說它們之間的關系,然后判斷這個人說的假話有多少句。
跟之前的思路,真話就它們歸到一個根節點,而它們和根節點的關系有3種同類,吃,被吃,關鍵的處理就在於路徑壓縮以及和根節點的關系,還有判定是不是假話。假如就設定一個動物和它根節點的關系為,0跟根節點同類 1被根節點吃 2吃根節點,那么在路徑壓縮時,
假如根節點是A,那么A<--B<--C,那么A<--C的關系是?通過觀察可以發現00->0 01->1 02->2 10->1 11->2 12->0 20->2 21->0 22->1,C與A的關系是由C與B的關系還有B與A的關系決定的,在這個問題中就有,當前點與根節點關系=(當前點與目前的上一節點的關系+目前的上一節點與根節點關系)%3 。
那在兩個動物的根節點連接在一起時該怎么確定,其中一方跟根節點的關系呢?這就需要向量加法假設B是根節點是A,D的根節點的C,加入要把C連到A,C與A的關系就有
A ← C
↑ ↑
B ← D
其中D到B的關系是給出的0或1,代表D與B是同類或D被B吃,B與A,D與C的關系都確定,再要求C與A的關系,其實可以很直觀的看出,就是C到D 的關系D到B的關系然后B到A的關系這樣這個向量加法,所以A ← C就是C→D加D->B加B->A,D->C的關系逆過來就是C->D了。同樣,若已經知道AB是同一根節點要判斷是不是假話,也是可以利用向量加法。
1 #include<stdio.h> 2 struct Animal{ 3 int root,is;//0跟根節點同類 1被根節點吃 2吃根節點 4 }A[51108]; 5 int gui(int x); 6 int bing(int z,int x,int y); 7 int main() 8 { 9 int i,n,m; 10 scanf("%d%d",&n,&m); 11 for(i=1;i<=n;i++) 12 { 13 A[i].root=i;//根節點初始為自己 14 A[i].is=0;//自己是自己的同類 15 } 16 int x,y,z,ans=0; 17 while(m--) 18 { 19 scanf("%d%d%d",&z,&x,&y); 20 if(x>n||y>n) 21 ans++; 22 else if(z==2&&x==y) 23 ans++; 24 else 25 ans+=bing(z,x,y); 26 } 27 printf("%d\n",ans); 28 } 29 int gui(int x) 30 { 31 if(A[x].root==x) 32 return x; 33 int y=A[x].root,z=gui(A[x].root);//注意要先調整上一節點與根節點的關系再來調整 34 A[x].is=(A[x].is+A[y].is)%3;//處理他們的關系 35 return A[x].root=z; 36 } 37 int bing(int z,int x,int y) 38 { 39 int bx=gui(x),by=gui(y); 40 if(bx==by)//X和Y有公共根節點,代表之前有真話把他們聯系在一起,此時方能判斷真假 41 { 42 if((A[y].is+3-A[x].is)%3!=z-1)//與公共根節點的關系判斷話的真假 43 return 1; 44 }//z-1就分別代表0 1 就xy同類還有y被x吃 45 else 46 { 47 A[by].root=bx; 48 A[by].is=(A[x].is+3-A[y].is+z-1)%3;//向量加法 49 } 50 return 0; 51 }