挑戰程序設計競賽 POJ 1192食物鏈 帶權並查集,略麻煩

食物鏈
Time Limit: 1000MS   Memory Limit: 10000K
Total Submissions: 66720   Accepted: 19675

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

/*
* poj 1182
* author  : mazciwong
* creat on: 2016-1-18
*/

/*
按A,B,C分類,並查集解決
並查集是維護 "屬於同一組"的數據結構
因爲有真有假,所有N個元素每個都可以分到三組中去
所以設出來的並查集有三個根,這三個是劃分塊,不能同時成立
每個劃分塊裏面是等價類,一個成立則其他都成立


*/


#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
using namespace std;
typedef long long ll;
const int maxn = 150000 + 5;
int n, k;
int d, x, y;
int fa[maxn];
int depth[maxn];
//maxn是最多的節點數,init中的n是用到的



//並查集初始化
void init(int n) 
{
	//包括用rank記錄樹的高度和路徑壓縮兩種優化
	for (int i = 0; i < n; i++)
	{
		fa[i] = i;
		depth[i] = 0;
	}
}

//並查集查詢
int find(int x)
{
	if (x == fa[x])
		return x;
	else
		return fa[x] = find(fa[x]);
}

//並查集合並
void unite(int x, int y)  
{
	x = find(x);
	y = find(y);
	if (x == y) return;
	if (depth[x] < depth[y]) //x根更短,則x指向y
		fa[x] = y;
	else if(depth[x]>depth[y])
		fa[y] = x;
	else //一樣高的話,誰指向誰都可以,記得高度加一
	{
		fa[y] = x;
		depth[x]++;
	}
}

//判斷是否屬於同一個集合
bool same(int x, int y)
{
	return find(x) == find(y);
}

/*
n個元素,每個元素都能放入A.B.C中,則共3n種對應法
用並查集來分組,在一組的話同時成立,相當於等價劃分塊
*/
void solve()
{
	int ans = 0;
	for (int i = 0; i < k; i++)
	{
		scanf("%d%d%d", &d, &x, &y);
		x--, y--;//把他們從1-n變成從0-(n-1),方便計算

		//題目裏給出的第二種錯誤
		if (x < 0 || x >= n || y < 0 || y >= n)
		{
			ans++;
			continue;
		}

		//下面是循環讀入,然後並查集處理
		if (d == 1) //x,y同一類
		{
			//就不能跟另外兩個同類了
			if (same(x, y + n) || same(x, y + 2 * n))
				ans++;
			else
			{ //要三個都寫是因爲後面兩個一定成立,x在A,y在A,那麼當x在B,y一定在B
				unite(x, y);
				unite(x + n, y + n);
				unite(x + 2 * n, y + 2 * n);
			}
		}
		else //x喫y
		{
			if (same(x, y) || same(x, y + 2 * n))
				ans++;
			else
			{
				unite(x, y + n);
				unite(x + n, y + 2 * n);
				unite(x + 2 * n, y);
			}
		}
	}
	printf("%d\n", ans);
}
int main()
{
	scanf("%d%d", &n, &k);
	init(n * 3);//把3*n種配對都初始化出來
	solve();
	return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章