這題有一個關鍵點: x的食物的食物以x爲食,即生物間的關係是以3爲循環的,就像運算 (0+1)%3=1,(1+1)%3=2,(2+1)%3=0,(0+1)%3=1... ...
不管d=1還是d=2,都表示x與y有關係,因此可以併到一個並查集裏去,然而具體的同類與捕食關係可用0,1,2來代表;
這裏以r[i]表示i與其並查集中父節點p[i]的關係: 0表示同類,1表示i吃p[i],2表是i被p[i]吃;
那麼如果x和y在同一個並查集時,可通過查詢x與根節點的關係,y與根節點的關係來判斷x與y的關係。若在並查集的find操作中壓縮路徑的話,根節點也就成了父節點,好方便!
另外合併和查找時可以利用一些加法,對3取模的運算來更新r。
AC代碼:
#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;
const int NN=50005;
int n;
int p[NN]; //根節點
int r[NN]; //與父節點的關係,0:同類,1:吃根節點,2:被根節點吃
inline void get(int &x)
{
char c=getchar();
while (c<'0' || c>'9') c=getchar();
x=c-'0';
c=getchar();
while (c>='0' && c<='9') x=x*10+c-'0',c=getchar();
}
int find(int x)
{
if (p[x]!=x)
{
int t=p[x];
p[x]=find(p[x]);
r[x]=(r[x]+r[t])%3; //x與新父節點(根節點)的關係
}
return p[x];
}
int Union(int d,int x,int y)
{
int fx=find(x);
int fy=find(y);
p[fx]=fy;
r[fx]=(r[y]-r[x]+2+d)%3; //fx與fy的關係=y與fy的關係和x與fx的關係差+x與y的關係
}
int main()
{
int d,x,y,k,cnt=0;
scanf("%d%d",&n,&k);
for (int i=1; i<=n; i++)
{
p[i]=i;
r[i]=0;
}
while (k--)
{
get(d); get(x); get(y);
if (x>n || y>n) cnt++;
else if (d==2 && x==y) cnt++;
else if (find(x)!=find(y)) Union(d,x,y);
else if ((r[y]+d+2)%3!=r[x]) cnt++;//這句話一定是在find(x)和find(y)之後的,因爲find過後r[x]纔是x與其根節點的關係
}
printf("%d\n",cnt);
return 0;
}