poj 1182 並查集經典問題

life4711發表於2014-08-10

http://poj.org/problem?id=1182

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 並查集經典問題——食物鏈(一組輸入)
題目大意:給定n個物種,和m個關係1.x和y是同類2.x吃y。問有多少個關係是假話
解題思路:由於n和m很大,所以必須高效的維護動物之間的關係,並快速的判斷是否產生了矛盾。並查集是維護“屬於同一組”的資料結構,但是在本題中,並不只有屬於同一類的
        資訊,還有捕食關係的存在,因此需要很好的維護這些關係。對於每隻動物i建立3個元素i-A,i-B,i-C,並用這3*N個元素建立並查集,這個並查集維護如下資訊:
        i-x表示:i屬於種類x;
        並查集裡的每一個組表示組內所有元素代表的情況都同時發生或不發生。
        例如:如果i—A和i-B在同一組裡,就表示如果i屬於種類A那麼j一定屬於種類B,如果j屬於種類B那麼i一定屬於種類A,因此對於每一條資訊,只要按照下面進行操作就可以
        了:
        第一種:x和y屬於同一類·····合併x-A和y-A、x—B和y—B、x-C和y-C
        第二種:x吃y···············合併x-A和y-B、x—B和y-C、x-C和y-A
        不過在合併之前要先判斷合併是否會產生矛盾。例如在第一種資訊的情況下,需要檢查比如x-A和y-B或者y-C是否在同一組等資訊。
                                                                                                                         ————摘自《挑戰程式設計競賽》
*/
#include <stdio.h>
#include <string.h>
#include <algorithm>
#include <iostream>
using namespace std;
const int maxn=50005;
int p[150010];

int find(int x)
{
    return p[x]==x ? x : p[x]=find(p[x]);
}

bool same(int x,int y)
{
    return find(x)==find(y);
}

void unite(int x,int y)
{
    int u = find(x);
    int v = find(y);
    if(u != v) p[u] = v;
}

int main()
{
    int n,m;
    scanf("%d%d",&n,&m);
    for(int i=1; i<3*n; i++) p[i] = i;
    int ans=0;
    while(m--)
    {
        int id,x,y;
        scanf("%d%d%d",&id,&x,&y);
        if(x>n||y>n)
        {
            ans++;
            continue;
        }
        if(id==1)
        {
            ///x and y the same
            if(same(x,y+n)||same(x,y+2*n))
            {
                ans++;
            }
            else
            {
                unite(x,y);
                unite(x+n,y+n);
                unite(x+n*2,y+n*2);
            }
        }
        else
        {
            /// x eat 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);
    return 0;
}

2015.5.19更新

相關文章