題目描述
動物王國中有三類動物 A,B,C,這三類動物的食物鏈構成了有趣的環形。A 喫 B,B喫 C,C 喫 A。
現有 N 個動物,以 1 - N 編號。每個動物都是 A,B,C 中的一種,但是我們並不知道
它到底是哪一種。
有人用兩種說法對這 N 個動物所構成的食物鏈關係進行描述:
第一種說法是“1 X Y”,表示 X 和 Y 是同類。
第二種說法是“2 X Y”,表示 X 喫 Y 。
此人對 N 個動物,用上述兩種說法,一句接一句地說出 K 句話,這 K 句話有的是真
的,有的是假的。當一句話滿足下列三條之一時,這句話就是假話,否則就是真話。
• 當前的話與前面的某些真的話衝突,就是假話
• 當前的話中 X 或 Y 比 N 大,就是假話
• 當前的話表示 X 喫 X,就是假話
你的任務是根據給定的 N 和 K 句話,輸出假話的總數。
輸入輸出格式
輸入格式:
從 eat.in 中輸入數據
第一行兩個整數,N,K,表示有 N 個動物,K 句話。
第二行開始每行一句話(按照題目要求,見樣例)
輸出格式:
輸出到 eat.out 中
一行,一個整數,表示假話的總數。
輸入輸出樣例
輸入樣例#1:
100 7
1 101 1
2 1 2
2 2 3
2 3 3
1 1 3
2 3 1
1 5 5
輸出樣例#1:
3
說明
1 ≤ N ≤ 5 ∗ 10^4
1 ≤ K ≤ 10^5
我們要判斷那些話是假話,就要看哪些話與之前說的不符
動物之間有喫,被喫,同類三種關係,我們只要把每一對x,y的關係記錄好,等到下一對時和前面的對比一下
看是否符合前面的關係就可以了,不符合的一定是假話,而且,對於任意三種動物xyz,我們只要知道xy,yz
這兩條關係,就可以知道這三種動物之間的所有關係了,是不是有點團伙,關押罪犯的影子,只不過這次敵人的
敵人有可能還是敵人,所以就用到了並查集,因爲要維護喫,被喫,同類三種關係,所以開三倍fa數組
(關押罪犯開了兩倍數組),開三個也行,別弄混了(話說我就弄混了各個數組的意思)來維護,具體看代碼
//可以先看一下關押罪犯再做這道題✿✿ヽ(°▽°)ノ✿
//開三倍數組版本
#include<iostream>
#include<cstdio>
using namespace std;
int n,k,tot=0;
int num,x,y;
int fa[300010];
//n:同類 2*n:自己喫 3*n:喫自己
int find(int x)
{
return fa[x]==x?x:fa[x]=find(fa[x]);
}
int main()
{
freopen("eat.in","r",stdin);
freopen("eat.out","w",stdout);
scanf("%d%d",&n,&k);
for(int i=1;i<=n*3;i++) fa[i]=i;
for(int i=1;i<=k;i++)
{
scanf("%d%d%d",&num,&x,&y);
if(x>n||y>n) //特判
{
tot++; continue;
}
else
{
int x1=find(x);//同類
int x2=find(x+n);//喫
int x3=find(x+n*2);//被喫
int y1=find(y);
int y2=find(y+n);
int y3=find(y+n*2);
if(num==1)
{
if(x==y) continue;
//之前判斷爲喫或者是被喫或是間接的關係,假話,tot++
if(x2==y1||y2==x1||x3==y2||x2==y3) tot++;
else//真·同類 更新祖先
{
fa[x1]=y1; //不能只更新x1,y1
fa[x2]=y2;
fa[x3]=y3;
}
}
else
if(num==2)
{
if(x==y) //特判,自己不能喫自己
{
tot++; continue;
}
else
if(x1==y1||y2==x1||x2==y3)//之前判斷爲同類或是y喫x(直接喫/間接喫)
{
tot++;
}
else
{ // 一定要加‘fa[]’=...
/*
fa[x2]=y1;//x喫y
fa[y3]=x1;//y被x喫
*/
fa[x1]=y3;//x喫y
fa[x2]=y1;//x喫y
fa[x3]=y2;//喫x的=y喫的 間接x喫y
}
}
}
}
printf("%d\n",tot);
return 0;
}
//其實多開幾倍數組就是一箇中轉站的作用,通過變爲某種動物的x1,x2,x3來更新自己的fa數組
//加權並查集不會啊,聽說,還要用向量,推公式,待補充...
//剛知道多開幾倍數組的並查集就是無意中看到的那個覺得高大上的擴展域並查集OMG
上圖是個遞歸啊,哇哇哇!