動物王國中有三類動物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句話有的是真的,有的是假的。
當一句話滿足下列三條之一時,這句話就是假話,否則就是真話。
1) 當前的話與前面的某些真的話衝突,就是假話;
2) 當前的話中X或Y比N大,就是假話;
3) 當前的話表示X喫X,就是假話。
你的任務是根據給定的N和K句話,輸出假話的總數。
輸入格式
第一行是兩個整數N和K,以一個空格分隔。
以下K行每行是三個正整數 D,X,Y,兩數之間用一個空格隔開,其中D表示說法的種類。
若D=1,則表示X和Y是同類。
若D=2,則表示X喫Y。
輸出格式
只有一個整數,表示假話的數目。
數據範圍
1≤N≤50000,
0≤K≤100000
輸入樣例:
100 7
1 101 1
2 1 2
2 2 3
2 3 3
1 1 3
2 3 1
1 5 5
輸出樣例:
3
去年就做過這個很經典的題,但那時候沒有真正理解,現在再來整理吧~
思路:我們要判斷每次給出的信息是否是假話必須判斷他們之間的關係,但是存儲兩兩之間的關係顯然是不現實的,那麼我們可存儲每個數與根節點的關係。
即我們可以把這些關係看成一棵樹,距離根節點爲1的就是可以喫根節點的,爲2的就是可以可以喫第1層的,爲3的和根節點是同類(3可以喫2, 1->2 , 2-> 3 那麼一定存在 3->1,所以爲3的一定是被1喫的,而根節點也是被1喫的,那麼根節點和3是同類),那麼以此類推,我們可以發現,以3爲循環,只要知道距離,我們就可以找出每個點與根節點的關係。
還有一個問題是:距離是什麼?每次我們發現有兩個動物x,y, 他們存在x->y (y可以喫x)的關係的時候,x和y就產生了一條距離爲1的邊。但是由於我們的樹太深了會導致超時,我們需要壓縮路徑,這時候就要處理好每個節點和根節點的距離。
首先,路徑壓縮的時候,我們正常的寫法是:
int find(int x)
{
if(x==pre[x])
return x;
return pre[x]=find(pre[x]);
}
由於我們多存儲了一個變量,即與根節點之間的距離,所以我們在壓縮路徑的時候要把距離也更新一下。
假如我們壓縮路徑的時候是這樣的,那麼此時d[x]代表到他的父節點的距離,壓縮完以後父節點變成了根節點,那麼我們讓d[x]加上父節點到根節點的距離即可。
int find(int x)
{
if(x!=pre[x])
{
int t=find(pre[x]);
d[x]+=d[pre[x]];
pre[x]=t;
}
return pre[x];
}
還有就是我們合併的時候,也要處理好距離,具體的看代碼註釋。
代碼:
#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
const int N = 5e4+10;
int pre[N],d[N];
int find(int x) //查找根節點+路徑壓縮
{
if(x!=pre[x])
{
int t=find(pre[x]);
d[x]+=d[pre[x]];
pre[x]=t;
}
return pre[x];
}
int main()
{
int n,m,ans=0;
cin>>n>>m;
for(int i=1;i<=n;i++) pre[i]=i;
while(m--)
{
int t,x,y;
cin>>t>>x>>y;
int fx=find(x),fy=find(y);
if(x>n || y>n) ans++;
else if(t==1) //x,y是同類
{
/*如果在同一棵樹上,根據與根節點的距離,判斷是否說了假話,如果他們之間
的距離%3爲0,說明是同類,如果不爲0,說明是假話*/
if(fx==fy && (d[x]-d[y])%3) ans++;
else if(fx!=fy)
{
/*合併的時候,要確保d[x]+d[fx]=d[y],他們纔是同類,所以d[fx]的值
要更新爲d[y]-d[x]*/
pre[fx]=fy;
d[fx]=d[y]-d[x];
}
}
else if(t==2) //x喫y的關係
{
/*如果在同一棵樹上,根據與根節點的距離,判斷是否說了假話,如果他們之間
的距離%3爲1,說明是同類,如果不爲1,說明是假話*/
if(fx==fy && (d[x]-d[y]-1)%3) ans++;
else if(fx!=fy)
{
/*合併的時候,要確保d[x]+d[fx]=d[y]+1,他們纔是同類(因爲x喫y,
所以x的距離要比y多1),所以d[fx]的值要更新爲d[y]+1-d[x]*/
pre[fx]=fy;
d[fx]=d[y]+1-d[x];
}
}
}
cout<<ans<<endl;
return 0;
}