(好的期末考試考砸的我滾回來寫代碼來了TAT)
(咦我什麼時候期末考試考好過~~)
卻說勇者有很多的親戚,親戚又有很多的親戚,親戚的親戚就是自己的親戚。
不幸的是,親戚中有些人不是很有錢,所以說會管勇者借錢。
勇者絲毫不介意啊,畢竟自從他討伐完大龍之後,他就日日有國王一個金幣的俸祿。
可是,終有一天,勇者發現一天之內,親戚就來了十多個。
還是路由器發現的,他將族譜整理完之後才告訴他說:“那些人有一半都不是你們家族的,就是過來詐騙的!”
勇者啞口無言,爲了拯救自己的金幣,他需要知道哪些人是自己的親戚。
——————————————
輸入格式:
第一行包含兩個整數N、M,表示共有N個人和M次操作。
接下來M行,每行包含三個整數Zi、Xi、Yi
當Zi=1時,表示Xi與Yi是親戚
當Zi=2時,輸出Xi與Yi是否是親戚,是的話輸出Y;否則話輸出N
輸出格式:
如上,對於每一個Zi=2的操作,都有一行輸出,每行包含一個大寫字母,爲Y或者N
提示:
親戚的親戚是親戚
——————————————
(題外話)
好的,我們接下來就是要講一個高深莫測玄幻之際的——並查集。
好吧在路由器沒學之前一直以爲並查集是huge佬所學的。
那麼今天將帶給蒟蒻也能看懂的並查集教程!
首先讓我們知道並查集的作用:簡短點說,就是需要完成的任務有1.合併集合2.查詢兩個元素是否在同一個集合內。
上面那句話一定要看懂再往下看(不然你會和路由器一樣當機半小時)
那麼,讓我們正式開始。
——————————————
勇者花費了一天的時間,終於找齊了所有七大姑八大姨了,那麼接下來就是需要靠古老魔法的幫助來完成族譜了。
路由器也在照相館中找到了魔法書,上面赫然寫着三個字。
並查集。
“並查集的實現是類似於樹一般,一整棵樹代表了一整個集合。這樣做的好處是,我們要想知道元素是否在同一個集合內,只需要看他們所在的集合的根節點是否一樣即可。”
“但是,這並非意味着圖論,因爲鏈式前向星有很大的空間限制。”
“我們用一個很簡單的方法實現建樹——fa數組(指father,不要想象成奇怪的東西)”
“fa數組的作用,fa[i]=j表示j是i的爸爸。規定根節點的爸爸爲根節點。”
“這樣,當我們知道了要合併的兩個元素i,j時,找到i與j的根節點if,jf,我們就可以簡單粗暴的用fa[if]= jf來實現。”
“那麼就只有一個問題了,查詢。”
“查詢方法:即不斷遞歸,就是fa套fa套fa套fa……直到fa[i]=i,這時候我們就找到了所在集合的根節點了。”
“然後比較這樣得到的根節點即可,相等,即爲一個集合……”
勇者“啪”的一聲跑了出去,然後用自己的魔法代碼功力敲完了魔法。
(但是這個魔法有缺陷,因此不貼出啦!實際上是懶~)
然而有一天,勇者氣喘吁吁的找到了路由器。
“路由器,那個魔法……他,不管用了!”
“啊?怎麼回事?”路由器疑惑問道。
“額……我把這個魔法賣給了精靈族,但他們第二天就要求退貨,說不靈了。”
“不靈到不至於,怕不是太慢。”路由器懶洋洋地說,“那到底怎麼回事啊?”
“因爲精靈能活千多歲……所以,”勇者支吾道,“他們將自己輸入,再把爸爸輸入,再爺爺,再太爺爺,再太太爺爺,再太太太爺爺,再……”
“行了,我知道什麼問題了,”路由器拜拜手,“你還是把書好好看看吧!”
勇者拿起書,將最後一點問題看完。
“……然而,有的時候,這樣做出來的樹實際上只有幾個支鏈,有的甚至沒有支鏈,那麼找的速度就會明顯增加。”
“但是解決的辦法也很簡單——我們先查一遍,然後把所有沿途遇到的元素的爸爸(fa)全設爲根節點即可。”
“以下是本書附帶魔法代碼,如有不懂參看註釋。”
#include<cstdio>
#include<cstring>
#include<iostream>
#include<cmath>
#include<algorithm>
using namespace std;
int fa[10001]={0};
int find(int a){//找到a所在的集合的根節點
if(fa[a]!=a)fa[a]=find(fa[a]);
//路徑壓縮優化
//當我們找到根節點的時候
//我們將這一路上我們往上爬的所有的節點
//全部指向這個根節點,這樣再搜就會很快
return fa[a];
//最開始的時候,我們碰到了第一個fa[a]==a
//此時我們就知道了fa[a]爲根節點
//將fa[a]的值傳遞給上一個fa[a],完成路徑壓縮
//經過壓縮之後到這個a,此時fa[a]就是根節點
}
void judge(int a,int b){//判斷是否在同一個集合裏
if(find(a)!=find(b)){
printf("N\n");
}else{
printf("Y\n");
}
return;
}
void unionn(int a,int b){//合併以a,b爲*根節點*與的兩個集合
fa[b]=a;//簡單粗暴的將兩棵樹連接起來
return;
}
int main(){
int n,m;//N個元素和M個操作
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++){
fa[i]=i;
//初始化,將每一個集合視爲一棵樹
//則開始時對於每一個元素,他的根節點就是他本身
}
for(int i=1;i<=m;i++){
int a1,b1,a,b,z;
scanf("%d%d%d",&z,&a,&b);
if(z==1){
a1=find(a);
b1=find(b);
if(a1!=b1){//不是同一個集合
unionn(a1,b1);
}
}else{
judge(a,b);
}
}
return 0;
}
下面附帶模板題:
洛谷 P3367 【模板】並查集:https://www.luogu.org/problem/show?pid=3367#sub
真的是模板,你只需要將上面的程序複製粘貼就AC啦!