這是一道分類並查集的題目。
我們要根據題目的要求,求出謊話的數目。題目給了我們一些明確的判斷標准:
但是我們會發現,如果只按照這三個條件和普通的並查集,我們只能得30分。
30分做法:我們可以用普通並查集和一個表示誰是誰的食物的數組來完成。
30分代碼:
#include<bits/stdc++.h> #define R register int #define M 500500 using namespace std; int n,k,fa[M],ans,eat[M]; inline int find(int x){return fa[x]= fa[x]==x ? x : find(fa[x]);} int main(){ ios::sync_with_stdio(0); int q,x,y; cin>>n>>k; for(R i=1;i<=n;++i) fa[i]=i; for(R i=1;i<=k;++i){ cin>>q>>x>>y; if(x>n || y>n) ++ans; else if(q==1){ if(eat[x]==y || eat[y]==x) ++ans; else if(find(x)!=find(y)) fa[find(x)]=find(y); } else{ if(x==y || eat[y]==x || find(x)==find(y) || eat[x]!=0) ++ans; else eat[x]=y; } } printf("%d",ans); return 0; }
滿分做法:
當我們再次仔細讀題,就會發現,題目中是有幾條隱含在題意里面的判斷條件的。
從這里我們可以看出一共只有3種動物,而且食物鏈是一個環。那么我們就可以得出這樣一條關系:如果x的食物是y,那么y的天敵是x,x的天敵是y的食物。這樣的話很顯然,一個數組是無法完成判斷的。而且我們並不知道,x和y分別是哪一種動物。
這時候我們就可以用種類並查集來實現這道題。種類並查集可以用來處理維護一些對立的關系,比如:敵人的敵人是朋友。我們通常會把並查集數組擴大n倍,n就是種類。所以這道題我們就可以開三倍的並查集數組,1到n表示種類A,n+1到n*2表示種類B,n*2+1到n*3表示種類C。然后我們就可以在合並的過程中維護各個點的信息了。
還有部分解釋在寫在代碼注釋里了哦↓ ↓ ↓ ↓ ↓ ↓ ↓
滿分代碼:
#include<bits/stdc++.h> #define R register int #define M 500500 using namespace std; int n,k,fa[M],ans; //n表示同類 n+n表示食物 n+n+n表示天敵 inline int find(int x){return fa[x]= fa[x]==x ? x : find(fa[x]);} inline void unity(int x,int y){fa[find(x)]=find(y);} int main(){ ios::sync_with_stdio(0); int q,x,y; cin>>n>>k; for(R i=1;i<=n*3;++i) fa[i]=i; for(R i=1;i<=k;++i){ cin>>q>>x>>y; if(x>n || y>n) ++ans; else if(q==1){ if(find(x+n)==find(y) || find(x+(n<<1))==find(y)) ++ans; //判斷x是y的同類是假話:y是x的食物 , y是x的天敵 else unity(x,y),unity(x+n,y+n),unity(x+(n<<1),y+(n<<1)); //如果x和y是同一個種類:那么x和y是同一種類,x和y的食物是同一種類,x和y的天敵是同一種類 } else{ if(x==y || find(x)==find(y) || find(x)==find(y+n)) ++ans; //判斷x吃y是假話:x能吃x(題目要求),x和y是同類的,x是y的食物 else unity(x,y+(n<<1)),unity(x+n,y),unity(x+(n<<1),y+n); //如果x吃y:y的天敵是x,y是x的食物,x的天敵是y的食物(因為一共只有三種動物,而且食物鏈是一個環形的,不理解的看一下代碼后面的圖片) } } printf("%d",ans); return 0; }