本来打算尝试学一下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