食物鏈
動物王國中有三類動物 A,B,C這三類動物的食物鏈構成了有趣的環形。
A 吃 B,B 吃 C,C 吃 A。
現有 N 個動物,以 1∼N 編號。
每個動物都是 A,B,C 中的一種,但是我們並不知道它到底是哪一種。
有人用兩種說法對這 NN 個動物所構成的食物鏈關係進行描述:
第一種說法是 1 X Y
,表示 X 和 Y 是同類。
第二種說法是 2 X Y
,表示 X 吃 Y。
此人對 N 個動物,用上述兩種說法,一句接一句地說出 KK 句話,這 KK 句話有的是真的,有的是假的。
當一句話滿足下列三條之一時,這句話就是假話,否則就是真話。
- 當前的話與前面的某些真的話衝突,就是假話;
- 當前的話中 X 或 Y 比 N 大,就是假話;
- 當前的話表示 X 吃 X,就是假話。
你的任務是根據給定的 N 和 K 句話,輸出假話的總數。
輸入格式
第一行是兩個整數 N 和 K,以一個空格分隔。
以下 KK 行每行是三個正整數 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
AC程式碼:
#include <iostream>
using namespace std;
const int Mn=50010;
//p[i]表示第i個節點的父節點,d[i]表示各個節點的距離;(自理解)
int p[Mn],d[Mn];
//程式碼核心,遞迴查詢父節點+路徑壓縮;
int find(int x){
if(p[x]!=x){
int t=find(p[x]);
//路徑壓縮:記錄子節點到祖先節點距離;實現類似直接讓當前節點直接指向祖先接節點;
d[x]+=d[p[x]];
//更新父節點:這個建議手推遞迴,就可以很清楚了;
p[x]=t;
}
return p[x];
}
int main(){
int n,m;
cin >> n >> m;
//初始化各個節點的父節點;
for(int i=0;i<n;i++){
p[i]=i;
d[i]=0;
}
int res=0; //記錄假話數量;
while(m--){
int c,x,y;
cin >> c >> x >> y;
//判斷當前的話中X或Y比N大,就是假話;
if(x>n || y>n) res++;
else{
int l=find(x),k=find(y);
//若c=1,則表示X和Y是同類。
if(c==1){
//合併的情況
if(l==k && (d[x]-d[y])%3) res++;
else if(l!=k){
p[l]=p[k];
d[l]=d[y]-d[x];
}
}else{
//合併情況;
//這裡為什麼要減一呢?因為是x吃y,如果在節點小一的情況下,能匹配為同類,則說明x就是吃y對的;
if(l==k && (d[x]-d[y]-1)%3) res++;
else if(l!=k){
p[l]=p[k];
d[l]=d[y]-d[x]+1;
}
}
}
}
cout << res << endl;
return 0;
}
大概構圖思想:
核心程式碼遞迴操作:
int find(int x){
if(p[x]!=x){
int t=find(p[x]);
//路徑壓縮:記錄子節點到祖先節點距離;實現類似直接讓當前節點直接指向祖先接節點;
d[x]+=d[p[x]];
//更新父節點:這個建議手推遞迴,就可以很清楚了;
p[x]=t;
}
return p[x];
}