1. 程式人生 > >luogu P1551 親戚(並查集入門)

luogu P1551 親戚(並查集入門)

這是一個並查集模板。

   說一下並查集,雖然我也是剛剛學會沒幾天。。。

並查集是個樹形結構的資料結構,主要用於合併兩個不相交的集合;

首先是初始化,其中的f陣列表示第i點的父親


for(int i=1;i<=n;i++)f[i] = i;

 並查集分為合併與查詢,我習慣寫成merge(合併),query(查詢)

合併的話,首先呼叫query函式去查詢兩點的祖先節點,如果不是同一祖先,兩個集合就沒有關聯,然後就將其祖先節點所代表的集合合併,(我所用的rand是讓合併的方向打亂,不易被卡),好像還有按秩合併,然而我還不會,以後再補這個鍋吧,,,


void merge(int x,int y){

    int f1 = query(x);

    int f2 = query(y);

    if(f1 != f2){

        if(rand()%2)f[f1] = f2;

        else f[f2] = f1;

    }

    return;

}

接下來就是query(查詢操作),不斷遞迴查詢父親節點,直到找到祖先節點,後面的是路徑壓縮,因為尋找路途上的所有點的祖先節點相同,所以在返回的時候把每個點去都放入同一集合,可以極大的縮短查詢時間,寫並查集的話一定帶上路徑壓縮,不然會被卡。不過好像有卡路徑壓縮的題,只是聽dalao說過,具體問題具體分析吧。


int query(int x){

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

    else return f[x] = query(f[x]);//路徑壓縮

}

下面是這道題,就是一個並查集根據題目要求做就可以了

題目背景

若某個家族人員過於龐大,要判斷兩個是否是親戚,確實還很不容易,現在給出某個親戚關係圖,求任意給出的兩個人是否具有親戚關係。

題目描述

規定:x和y是親戚,y和z是親戚,那麼x和z也是親戚。如果x,y是親戚,那麼x的親戚都是y的親戚,y的親戚也都是x的親戚。

輸入輸出格式

輸入格式:

第一行:三個整數n,m,p,(n<=5000,m<=5000,p<=5000),分別表示有n個人,m個親戚關係,詢問p對親戚關係。

以下m行:每行兩個數Mi,Mj,1<=Mi,Mj<=N,表示Mi和Mj具有親戚關係。

接下來p行:每行兩個數Pi,Pj,詢問Pi和Pj是否具有親戚關係。

輸出格式:

P行,每行一個’Yes’或’No’。表示第i個詢問的答案為“具有”或“不具有”親戚關係。

輸入輸出樣例

輸入樣例#1: 複製

6 5 3

1 2

1 5

3 4

5 2

1 3

1 4

2 3

5 6

輸出樣例#1: 複製

Yes

Yes

No

附AC程式碼:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cstdlib>

using namespace std;
const int maxn = 500521;
int n,m,p;
int mi;
int mj;
int pi;
int pj;
int f[maxn];
inline int read(){//讀入優化
    int x=0;int f=1;char c=getchar();
    while(c<'0'||c>'9'){
        if(c=='-')f=-f;
        c=getchar();
    }
    while(c<='9'&&c>='0'){
        x=x*10+c-'0';
        c=getchar();
    }
    return x*f;
} 
int query(int x){//查詢操作
    if(x == f[x])return x;
    else return f[x] = query(f[x]);//路徑壓縮
}
void merge(int x,int y){//合併操作
    int f1 = query(x);
    int f2 = query(y);
    if(f1 != f2){
        if(rand()%2)f[f1] = f2;
        else f[f2] = f1;
    }
    return;
}
int main(){
    n = read();
    m = read();
    p = read();
    for(int i=1;i<=n;i++)f[i] = i;//初始化,不能丟
    for(int i=1;i<=m;i++){
        mi = read();
        mj = read();
        merge(mi,mj);
    }
    for(int i=1;i<=p;i++){
        pi = read();
        pj = read();
        if(query(pi) == query(pj)){
            printf("Yes\n");
        }
        else printf("No\n");
    }
    
    return 0;
}