本来打算尝试学一下LCA的,但是发现似乎离线算法需要预备知识:并查集,于是尝试学了一下并查集。
并查集的思想:
有时候我们需要对一个集合进行操作,比如判断某个数是否属于另一个数所属于的某个集合,那么我们就可以用这样的思想:
每一个集合只存一个代表,集合中的其他元素都连接这个代表,而这个集合中除了代表连接着其他元素,其他元素不互相连接。
这样的话,速度就快很多了。比如原来我们可能需要这样:
对于一个集合大小为n的集合,时间复杂度为O(n)
而使用并查集后,复杂度为O(1)
代码:
#include<cstdio>
#include<iostream>
using namespace std;
int data[10001];
int find(int k){
if(data[k]==k)return k;
return data[k]=find(data[k]);
}
int main(){
int n,m;
cin>>n>>m;
for(int i=1;i<=n;i++){//n个元素
data[i]=i;
}
for(int i=0;i<m;i++){//m个操作
int a,b,c;
cin>>a>>b>>c;
if(a==1){
data[find(data[b])]=find(c);//将b的爸爸变成c的爸爸 注意,这里需要让b的最大的爸爸的爸爸变成c的最大的爸爸。在find的同时就会更新每个元素的爸爸为当前的总爸爸(return时的处理)
}else if(a==2){
if(find(b)==find(c)){
cout<<"Y"<<endl;
}else{
cout<<"N"<<endl;
}
}
}
}
关键的方法就是find(int k)方法。重点是对这个方法的理解,剩余的内容主要是输入的进行。对这个方法的理解可以看一下洛谷的题解。
我对find(int k)方法的理解:
第一行{ if(data[k]==k)return k; }:k的值为要查询的值,方法本身的用处是查找k的最大的父亲,那么这一行的意思就是:如果k的父亲是他自己,就返回他自己。
第二行{ return data[k]=find(data[k]); }:如果k的父亲不是k自己,就查找 k的父亲 的最大的父亲,并设置k的最大的父亲为 k的父亲 的最大的父亲。这样就可以得出,从k到k次一级的父亲都会直接变成最大的父亲的儿子。
题目(洛谷):https://www.luogu.org/problemnew/show/P3367
题解(洛谷):https://www.luogu.org/problemnew/solution/P3367