1. 程式人生 > >並查集擴充套件域 —— [NOI2001]食物鏈

並查集擴充套件域 —— [NOI2001]食物鏈

 並查集擴充套件域的思想:

  • 有時,我們需要在使用並查集的同時,表示集合中元素的某些關係(這些關係類似有向邊)
  • 直接使用並查集顯然是不行的,因為並查集只能保證某些元素在或不在同一個集合中,而如果需要表示關係,就不行了
  • 這時候就可以嘗試擴大關係種類數量的集合數量,比如:給出的元素是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