1. 程式人生 > >種類並查集+入門題A Bug's Life

種類並查集+入門題A Bug's Life

我覺得種類並查集還是先從一個基礎入門題講起吧。

Background
Professor Hopper is researching the sexual behavior of a rare species of bugs. He assumes that they feature two different genders and that they only interact with bugs of the opposite gender. In his experiment, individual bugs and their interactions were easy to identify, because numbers were printed on their backs.
Problem


Given a list of bug interactions, decide whether the experiment supports his assumption of two genders with no homosexual bugs or if it contains some bug interactions that falsify it.

Input

The first line of the input contains the number of scenarios. Each scenario starts with one line giving the number of bugs (at least one, and up to 2000) and the number of interactions (up to 1000000) separated by a single space. In the following lines, each interaction is given in the form of two distinct bug numbers separated by a single space. Bugs are numbered consecutively starting from one.

Output

The output for every scenario is a line containing "Scenario #i:", where i is the number of the scenario starting at 1, followed by one line saying either "No suspicious bugs found!" if the experiment is consistent with his assumption about the bugs' sexual behavior, or "Suspicious bugs found!" if Professor Hopper's assumption is definitely wrong.

Sample Input

2
3 3
1 2
2 3
1 3
4 2
1 2
3 4

Sample Output

Scenario #1:
Suspicious bugs found!

Scenario #2:
No suspicious bugs found!

Hint

Huge input,scanf is recommended.

#include <algorithm>
#include <iostream>
#include <string>
#include <vector>
#include <cstring>
#include <cstdio>
#include <stack>
#include <set>
#define MAXN 2004
using namespace std;

int f[MAXN],r[MAXN];
bool flag;

int getf(int x){
    if(x == f[x]) return x;
    int t = getf(f[x]);
    r[x] = (r[x] + r[f[x]]) % 2;
    f[x] = t;
    return t;
}

void Union(int a,int b){
    int fa = getf(a);
    int fb = getf(b);
    if(fa == fb){
        if(r[a] == r[b]){
            flag = false;
        }
        return;
    }
    f[fb] = fa;
    r[fb] = (r[a]+1-r[b]+2)%2;
}

int main(){
    int t;
    scanf("%d",&t);
    for(int i=1;i<=t;++i){
        int n,m;
        scanf("%d%d",&n,&m);
        for(int i=1;i<=n;++i){
            f[i] = i;
            r[i] = 0;
        }
        flag = true;
        int a,b;
        for(int i=0;i<m;++i){
            scanf("%d%d",&a,&b);
            if(flag) Union(a,b);
        }
        printf("Scenario #%d:\n",i);
        if(flag) cout << "No suspicious bugs found!" << endl;
        else cout << "Suspicious bugs found!" << endl;
        cout << endl;
    }
    return 0;
}

其實乍一看,種類並查集和基礎並查集大致一樣,無非就是多了兩句程式碼:getf函式中的 r[x] = (r[x] + r[f[x]]) % 2 和 Union函式中的 r[fb] = (r[a]+1-r[b]+2)%2,不用多想,這兩句就是種類並查集的關鍵,所以在這裡就重點解釋這兩句的意思。

首先,就這道題而言,很明顯昆蟲之間就兩種關係,同性和異性,我們分別標記為0 和 1,用 r 陣列存放,具體點說就是 r[i] == 0 表示 i 跟 f[i] 是同性,反之異性。

首先我們定義一種關係:f[a] -> a 代表 r[a] ,記準這個,在以後的推理中很有幫助。

在getf函式中,我們假設最終的根節點為root(定義為root只是為了幫助理解,這裡不用多想,往下看就行了),然後在壓縮路徑的同時,首先看到關鍵程式碼:r[x] = (r[x] + r[f[x]]) % 2,這一句其實是實時更新 r[x], 因為我們知道,在壓縮路徑的同時,x的直接父節點f[x]可能就會變了(變的話就是變為root),所以r[x]也可能隨之改變,怎麼變呢?是不是可以這樣表示:root->x = root->f[x] + f[x]->x。有可能你會問,root也不一定是f[x]的直接父節點啊,記住,因為路徑壓縮是遞迴的,所以在此之前,root已經是f[x]的直接父節點了,所以root->f[x] 這一句是沒毛病的。根據我們之前的關係將這句翻譯過來不就是r[x] = (r[x] + r[f[x]]) % 2,至於為什麼%2,是因為在這個題中我們只定義了0,1兩種關係,正確性可以自己驗證。

在Union函式中涉及了兩種情況:fa == fb,即a,b根節點相同,屬於同一棵樹,只判斷a和b跟根節點的關係是否相同就行了,即r[a] == r[b]。

fa != fb時,分屬兩棵樹,需要將它們聯絡在一起,做法就是將一個根節點掛在另一個根節點上,即f[fb] = fa,同時更新 r陣列的關係,怎麼更新?我們的做法是將fb掛在fa上,根據我們定義的關係,可以這樣表示 fa->fb = fa->a + a->b + b->fb,翻譯過來就是r[fb] = (r[a] + 1 - r[b] + 2) %2,其中 + 1是題目暫時給出的關係,為異性,+2是避免負數。

大致就是這麼個意思吧。我覺得之前說的要記準的那個關係我覺得挺好用的,推理很方便,還有要記得活用,這個就廢話了,還是要多多練習。