1. 程式人生 > >做題記錄:P2024 食物鏈(洛谷)

做題記錄:P2024 食物鏈(洛谷)

節點 找到 find merge 如果 div clas spa blank

P2024 食物鏈

/*思路:並查集,因為一開始我們並不知道每一只動物是哪一個種類的,
所以我們幹脆建立三倍於n的空間,1~n這三分之一用來存第i只動物是A
的情況,n+1~2n這三分之一用來存第(i-n)只動物是B的情況,2n+1~3n這
三分之一用來存這只動物是C的情況。對於每一句話給出的關系,我們並
不知道其中的兩個動物分別是屬於哪一個種類的,所以我們就把每一種情
況都處理一遍。當兩個屬於同一個區間的動物被合並時,就代表它們是同
一個種類的;當兩個屬於不同區間的動物被合並時,就代表作為父節點的
那一只動物吃作為子節點的那一只動物。照這樣處理每一句真話即可。判
斷假話時,第2、3種假話是很容易判斷的,若是第一種假話就這樣判斷:
如果說的是x與y是同類的話,就判斷x有沒有和不在同一區間的y合並(這
代表x吃y)以及y有沒有和不在同一區間的x合並(這代表y吃x),如果有,
這就是假話;如果說的是x吃y,就判斷x有沒有和在同一區間的y合並(這代
表x和y是同類)以及y有沒有和不在同一區間的x合並(這代表y吃x),如
果有,這就是假話。最後輸出答案即可。
*/ #include<iostream> #include<cstdio> #include<fstream> #include<algorithm> #include<cmath> #include<queue> using namespace std; int f[150005];//f[1~n]存動物A,f[n+1~2n]存動物B,f[2n+1~3n]存動物C int find_(int x)//並查集找祖先函數 { if(f[x]==x) return x;//找到了祖先就返回祖先是哪一個 return
f[x]=find_(f[x]);//路徑壓縮+查找 } void merge_(int x,int y)//並查集合並函數 { int t1=find_(x),t2=find_(y);//找各自的祖先 if(t1!=t2) f[t2]=t1;//如果不在同一個集合內就合並 return;//結束 } int main() { int n=0,k=0,ans=0; scanf("%d%d",&n,&k); for(int i=1;i<=n*3;i++) f[i]=i;//並查集初始化 for(int i=1;i<=k;i++) {
int s=0,x=0,y=0; scanf("%d%d%d",&s,&x,&y); if(x>n||y>n||s==2&&x==y) ans++;//判斷屬於第2、3種情況的假話 else if(s==1)//如果此句話說的是x和y為同類 //如果前面的話中已經出現x吃y或y吃x的情況,就說明它們不是同類且這一句話是假話 if(find_(x)==find_(y+n)||find_(y)==find_(x+n)) ans++; else//否則就說明這一句話是真的 { //考慮3種情況 merge_(x,y);//若它們均為動物A,則合並x,y merge_(x+n,y+n);//若它們均為動物B,則合並x+n,y+n merge_(x+n*2,y+n*2);//若它們均為動物B,則合並x+n*2,y+n*2 } else//如果此句話說的是x吃y //如果前面的話中已經出現x與y是同類或y吃x的情況,就說明x不吃y且這一句話是假話 if(find_(x)==find_(y)||find_(y)==find_(x+n)) ans++; else//否則就說明這一句話是真的 { //考慮3種情況 merge_(x,y+n);//若x為A且y為B,將y+n合並到x下,表示x吃y+n merge_(x+n,y+n*2);//若x為B且y為C,將y+n*2合並到x+n下,表示x+n吃y+n*2 merge_(x+n*2,y);//若x為C且y為A,將y合並到x+n*2下,表示x+n*2吃y } } printf("%d",ans);//輸出 return 0; }

做題記錄:P2024 食物鏈(洛谷)