並查集擴充套件域 —— [NOI2001]食物鏈
阿新 • • 發佈:2018-11-10
並查集擴充套件域的思想:
- 有時,我們需要在使用並查集的同時,表示集合中元素的某些關係(這些關係類似有向邊)
- 直接使用並查集顯然是不行的,因為並查集只能保證某些元素在或不在同一個集合中,而如果需要表示關係,就不行了
- 這時候就可以嘗試擴大關係種類數量的集合數量,比如:給出的元素是1 2 3 4,而此時有兩種關係,我們就可以建立三倍的集合(
可能這樣描述的不清楚),也就是這樣:(1 2 3 4)(1 2 3 4)(1 2 3 4)。而這些數字我們可以儲存到同一個陣列中,用下標來確定他們所在的集合。由於在預設情況下,不同的集合之間是不會合並的,那麼,如果不同的集合之間合併了,就可以代表他們之間有關係(因為我們只在需要表示關係的時候才對不同集合進行合併)
並查集擴充套件域的實現過程:
- 初始狀態:
- 普通合併(不表示關係):
- 表示點2與點4之間存在關係(圖中紅線代表有向邊,我們具體實現的話,就把紅線兩邊的點直接進行合併即可。由於當我們查詢兩個點是否在一個集合時,只會在A群系中查詢,所以不會影響普通的查詢,只有在檢視點2和點4的關係時,我們才會跨群系查詢):
- 接下來的過程同理。根據需要進行同群系或跨群系合併即可。
題目連結:https://www.luogu.org/problemnew/show/P2024
#include<cstdio> #include<iostream> using namespace std; int n,fa[100005*5]; int find(int a){ while(a!=fa[a]){ a=fa[a]=fa[fa[a]]; } return a; } void setTL(int x,int y){ fa[find(x)]=find(y); fa[find(x+n)]=find(y+n); fa[find(x+2*n)]=find(y+2*n); } void setChi(int x,int y){ fa[find(x)]=find(y+n); fa[find(x+n)]=find(y+2*n); fa[find(x+2*n)]=find(y); } int main(){ int k; scanf("%d%d",&n,&k); for(int i=1;i<=n*4;i++){ fa[i]=i; //fa[n+i]=n+i; //fa[2*n+i]=2*n+i; } int jhsum=0; for(int i=1;i<=k;i++){ int a,x,y; scanf("%d%d%d",&a,&x,&y); if(x>n||y>n){ jhsum++;continue; } if(a==2)if(find(x)==find(y)){//如果x和y是同類 jhsum++;continue; } if(a==1){ if(find(x)==find(n+y)||find(x)==find(2*n+y)){ jhsum++;continue; } setTL(x,y); }else if(a==2){ if(find(x)==find(y+2*n)){ jhsum++;continue; } setChi(x,y); } } printf("%d\n",jhsum); return 0; }
歡迎加入我們的OI討論群
群號:849352599