1. 程式人生 > >並查集的拓展域

並查集的拓展域

  並查集擅長的是動態維護圖中具有傳遞性的關係。有的時候,我們需要傳遞的關係比較單一,但有的時候,傳遞的關係會比較複雜。這時候就需要用到並查集的擴充套件域。        看例題:食物鏈    

題目描述

動物王國中有三類動物 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 句話有的是真

的,有的是假的。當一句話滿足下列三條之一時,這句話就是假話,否則就是真話。

• 當前的話與前面的某些真的話衝突,就是假話

• 當前的話中 X 或 Y 比 N 大,就是假話

• 當前的話表示 X 吃 X,就是假話

你的任務是根據給定的 N 和 K 句話,輸出假話的總數。

分析:

這道題裡,關係就有很多種,x與y可能是同類,也可能是x吃y,或者y吃x;

我們在做並查集的操作時,是把相互關聯的點合併,每次有新的操作時,判斷有沒有衝突,然後再做下一步;這道題如果我們只是單純的把x當做一個點,那我們沒法兒維護這中間的關係。想一下,x的同類是一種關係,我們需要把他們合併,x的所有天敵是一類,也需要合併,x的食物也是一類,同樣需要合併。但是這三種合併又不能用一個點來進行。所以我們把每個點拆開,變成三個點。分別是Xself,Xeat,Xenemy。

如果x與y是一類,那麼Xself與Yself合併,Xeat與Yeat合併,Xenemy與Yenemy合併。

如果x吃y,那麼Xeat與Yself合併,Yenemy與Xself合併,Xenemy與Yeat合併(因為只有三種動物,如果x吃y,y吃z,那麼z吃x);

在執行合併之前要先判斷有沒有衝突,關於x=y有兩種資訊衝突:

1、x吃y;即Xeat=Yself

2、y吃x;即Yeat=Xself

關於x吃y有兩種衝突:

1、x與y是一類,即Xself=Yself

2、y吃x,即Yeat=Xself

如果資訊有衝突,那麼這句話就是假話。否則,執行合併;

程式碼:

#include<bits/stdc++.h>

using namespace std;

int 

n,m,ans,q,x,y,a,b,Xself,Yself,Xeat,Yeat,Xen,Yen,fa[150010];

 

int get(int x)

{

    if(fa[x]==x) return fa[x];

    return fa[x]=get(fa[x]);

}

 

void Merge(int a,int b)

{

    fa[get(a)]=get(b);

}

 

int main()

{

    scanf("%d %d",&n,&m);

    for(int i=1;i<=3*n;i++) fa[i]=i;

    for(int i=1;i<=m;i++){

        scanf("%d %d %d",&q,&x,&y);

        Xself=(x-1)*3+1;Xeat=(x-1)*3+2;Xen=x*3;

        Yself=(y-1)*3+1;Yeat=(y-1)*3+2;Yen=y*3;

        if(x>n||y>n){

            ans++;continue;

        }

        if(q==1){

            if(get(Xself)==get(Yeat)||get(Xeat)==get(Yself)){

                ans++;continue;

            }

            Merge(Xself,Yself);

            Merge(Xen,Yen);

            Merge(Xeat,Yeat);

        }

        else{

            if(get(Xself)==get(Yself)||get(Yeat)==get(Xself)){

                ans++;continue;

            }

            Merge(Xeat,Yself);

            Merge(Xself,Yen);

            Merge(Xen,Yeat);

        }

    }

    cout<<ans<<endl;

    return 0;

}