Loading

并查集

并查集是一种只关心元素对于集合的所属关系的数据结构。
它的原型是一棵树,使用 “父亲表示法” 表示,用数组 Fa[i] 保存 i 的父亲的编号。
每个孩子都有一个父亲,而一个家族的祖先便是这个家族的标识。
如果你我有着同样的祖先,则你和我是一个家族的。
特别的,祖先的父亲是自己。

并查集善于添加关系。
如果涉及到破化关系,可以考虑反向建边。
\(\sf\hspace{5cm}-David\)
\(\color{lightgray}\textsf{2023-1-31 Updated}\)

一般的并查集

初始化

先认为每个元素自成集合,即 Fa[i]=i ,并集的时候在把父亲设为目标集合的祖先。

找父亲

由于并查集只关心所属关系,所以我爸是你或你爸是我没什么区别。所以,我们在找祖先时可以做路径优化,就是忽略之前所有的父子关系,统一认为一个家族所有人都是这个家族祖先的直接孩子,包括祖先自己。
使用递归函数向上找父亲、祖父、曾祖父……
回溯的时候顺便把父亲的父亲设为找到的那个祖先。

int Find(int n)
{
	if(Fa[n]!=n)	Fa[n]=Find(Fa[n]);
	return Fa[n];
}

并集

只需要操作一下祖先即可。

void Merge(int a,int b)
{
	a=Find(a),b=Find(b);
	Fa[b]=a;
}

查集

如果你我有着同样的祖先,则你和我是一个家族的。

bool Judge(int a,int b)
{
	a=Find(a),b=Find(b);
	return (a==b);
}

拓展域并查集

有些时候,
我们不仅知道某些与某些在一个集合中,
我们还知道某些与某些不在一个集合中,
甚至知道某些和某些在相邻的两个集合中。

怎么办呢?
这个时候,我们就要请出“反我”了。
让我和“反我”始终不在一个集合之中,如果知道某某与我不在一个集合之中,就说明此某与“反我”在一个集合,我与“反此某”在同一集合中。
就巧妙地转化了问题。

封装一下?

正常并查集

template<int maxlength>
class DisjointSets
{
	private:
		int Fa[maxlength+5];
	public:
		DisjointSets()
		{
			for(int i=0;i<maxlength;i++)
				Fa[i]=i;
		}
		DisjointSets(DisjointSets<maxlength>&m)
		{
			for(int i=0;i<maxlength;i++)
				Fa[i]=m.Fa[i];
		}
		~DisjointSets(){}
		int Find(int n)
		{
			if(n<0||n>=maxlength)
				return -1;
			if(Fa[n]!=n)
				Fa[n]=Find(Fa[n]);
			return Fa[n];
		}
		void Merge(int a,int b)
		{
			if(a<0||a>=maxlength||b<0||b>=maxlength)
				return;
			Fa[Find(b)]=Find(a);
			return;
		}
		bool Judge(int a,int b)
		{
			if(a<0||a>=maxlength||b<0||b>=maxlength)
				return false;
			return Find(a)==Find(b);
		}
		void operator=(DisjointSets m)
		{
			for(int i=0;i<maxlength;i++)
				Fa[i]=m.Fa[i];
		}
		int& operator[](int i){return Fa[i];}
};

拓展域并查集

template<int maxlength,int extent>
class ExjointSets
{
	private:
		int Fa[extent*maxlength+30];
		int get(int x,int y){return x*maxlength+y;}
		int find(int n)
		{
			if(Fa[n]!=n)
				Fa[n]=find(Fa[n]);
			return Fa[n];
		}
		void merge(int a,int b){Fa[find(b)]=find(a);return;}
		bool judge(int a,int b){return find(a)==find(b);}
	public:
		ExjointSets()
		{
			const int amount=extent*maxlength;
			for(int i=0;i<amount;i++)
				Fa[i]=i;
		}
		ExjointSets(ExjointSets<maxlength,extent>&m)
		{
			const int amount=extent*maxlength;
			for(int i=0;i<amount;i++)
					Fa[i]=m.Fa[i];
		}
		~ExjointSets(){}
		int Find(int n,int a)
		{
			if(n<0||n>=maxlength||a<0||a>=extent) 
				return -1;
			return find(get(a,n));
		}
		void Merge(int a,int b,int dif)
		{
			if(a<0||a>=maxlength||b<0||b>=maxlength)
				return;
			if(dif<0||dif>=extent)
				return;
			for(int i=0;i<extent;i++)
				merge(get(i,a),get((i+dif)%extent,b));
			return;
		}
		bool Judge(int a,int b,int dif)
		{
			if(a<0||a>=maxlength||b<0||b>=maxlength)
				return false;
			if(dif<0||dif>=extent)
				return false;
			return judge(get(0,a),get(dif,b));
		}
};
posted @ 2022-11-19 16:58  PCwqyy  阅读(17)  评论(0)    收藏  举报  来源