Description
動物王國中有三類動物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(1 <= N <= 50,000)和K句話(0 <= K <= 100,000),輸出假話的總數。
Input
第一行是兩個整數N和K,以一個空格分隔。
以下K行每行是三個正整數 D,X,Y,兩數之間用一個空格隔開,其中D表示說法的種類。
若D=1,則表示X和Y是同類。
若D=2,則表示X喫Y。
Output
只有一個整數,表示假話的數目。
Sample Input
100 7
1 101 1
2 1 2
2 2 3
2 3 3
1 1 3
2 3 1
1 5 5
Sample Output
3
思路:並查集。x,x+N,x+2N分別是編號x的動物爲A/B/C類的可能性。每給出一句話,將同時成立的可能性加入一個並查集,判斷是否矛盾。
AC代碼:
#include <iostream>
#include <stdio.h>
#include <algorithm>
#include <cstdlib>
#define N 50000 //最多50000個動物
using namespace std;
int i,n,k; //n個動物,k句話
int x,y;
int par[3*N]; //父親
int rank[3*N]; //樹高
//並查集
int find(int x) //找根
{
if(par[x]==x) return x;
else return par[x]=find(par[x]);
}
bool same(int x0,int y0) //是否同集
{
return find(x0)==find(y0);
}
void unite(int x,int y)
{
x=find(x);
y=find(y);
if(x==y) return;
else{
if(rank[x]>rank[y]) par[y]=x;
else{
par[x]=y;
if(rank[x]==rank[y]) rank[y]++;
}
}
}
int main()
{
int d,ans=0; //d:1或2
for(i=0;i<N*3;i++) //x,x+N,x+N*2是x分別屬於A,B,C組的可能性
{
par[i]=i;
rank[i]=0;
}
scanf("%d %d",&n,&k);
getchar();
for(i=0;i<k;i++){
scanf("%d %d %d",&d,&x,&y);
getchar();
x=x-1,y=y-1;
if(x<0 || y<0 || x>=n || y>=n){
ans++;
continue;
}
if(d==1){
if(same(x,y+N) || same(x,y+N*2)){
ans++;
continue;
}
else{
unite(x,y); //x屬於A組和y屬於A組會同時發生或同時不發生
unite(x+N,y+N);
unite(x+N*2,y+N*2);
}
}
if(d==2){
if(same(x,y) || same(x,y+N*2)){
ans++;
continue;
}
else{
unite(x,y+N);
unite(x+N,y+N*2);
unite(x+N*2,y);
}
}
}
cout<<ans<<endl;
return 0;
}