加载中...

带权并查集,扩展域并查集

带权并查集

本质上就是并查集树上的边有了边权,进而可以用一个 \(dist\) 数组来表示每个点与其所在集合的祖先之间的关系;

  • 合并:在任意两个点有了新的关系后,可以通过直接合并二者所在集合的祖先来构建二者之间的关系;
  • 查询:查询同一集合内任意两点之间的关系也可以通过二者和祖先之间的关系推导出来;不同集合内的两个点是没有任何关系的
  • 在查询每个点的祖先(find)过程中,边做路径压缩,边合并 dist 的信息

可以用来解决 维护并查集每个集合内任意两点之间信息 的问题。

模板:

// 带权并查集
int fa[100002], siz[100002];
int dist[100002];
void init(int n){
	for(int i = 1; i <= n; i ++){
		fa[i] = i;
		dist[i] = 0;
        siz[i] = 1;
	}
}
int find(int x){ // 路径压缩的同时,合并dist的信息
	if(x != fa[x]){
		int tmp = fa[x];
		fa[x] = find(tmp);
		dist[x] += dist[tmp];
	}
	return fa[x];
}
bool union_(int l, int r, int k){ // 设置 l 到 r 在一维数轴上的距离为 v
	int lf = find(l), rf = find(r);
	if(lf != rf){ // 只有二者不在同一个集合中才能合并
		fa[lf] = rf;
        siz[rf] += siz[lf];
        siz[lf] = 0;
		dist[lf] = dist[r] - dist[l] + k;
		return true; 
	}
	return false;
}
int query(int u, int v){ // 查询 u 与 v 之间的相对距离(只有u, v在同一集合中才有解)
	if(find(u) != find(v)) return INF;
	return dist[u] - dist[v];
}

扩展域并查集

本质上就是好几个并查集,其中每个并查集用于表示某个种类。一个个体可以是这几个种类中的某一种,而种类之间可能存在着一些关系;而题目并不给出这些个体是哪个种类,而会给出每一对个体之间的关系。这时就可以通过枚举这两个个体可能的所有种类的情况,并将其合并起来。

举个例子:有 3 种动物种类 \(A,B,C\),三者形成一种循环食物链:A 吃 B,B 吃 C,C 吃 A。

则我们需要对于每个种类均初始化一个并查集,而不同并查集内的个体是需要参与合并的。

现在有两个动物 x, y, 规定 x 吃 y,那么,我们需要做:

  • A 中的 x 与 B 中的 y 合并
  • B 中的 x 与 C 中的 y 合并
  • C 中的 x 与 A 中的 y 合并

而若想查询 z 是否吃 w,或者检验某个规定是否与之前的规定产生矛盾,就可以直接利用上面维护的扩展域并查集来检查。更详细的可以看这篇博客:blog

扩展域并查集判断二分图

现共有 \(n\) 个点,\(m\) 条边,判断当前图是否为二分图。除了 dfs 染色法等其他方法,我们还可以用扩展域并查集来判断。

具体地,对编号 \(1\backsim n\) 每个点,我们额外添加 \(1',2'...n'\) 共 \(n\) 个点(编号为 \(n + 1 \backsim 2n\)),其中 \(i\) 和 \(i'\) 分别表示点 \(i\) 在集合 \(1\), 集合 \(2\)(二分图中总共只有 \(2\) 个集合)。显然,对于 \(\forall i\),\(i\) 与 \(i'\) 是天然矛盾的,因为一个点仅会属于某一个集合。初始时这 \(2n\) 个点均属于各自的 \(2n\) 个集合,现在有一条连边 \((u, v)\),由二分图定义可知,\(u, v\) 一定属于不同的集合,那么这句话在扩展域并查集中的等价定义其实就是:\(u, v'\) 属于同一集合,且 \(u', v\) 属于同一集合。 于是,对于每条连边,在并查集中就等价于做两次合并操作。

在二分图中,我们会发现:边的数量越多,这个图是二分图的可能性就会越低。因此,在我们逐次添加边的过程中,总会有某一次加边后,使得这个图之前是二分图,加边后就不再是二分图。如何判断这一次加边呢?很简单:对于加边 \((u, v)\),如果发现 \((u, v)\) 当前已经属于同一个集合了,那也就意味着 \((u, v)\) 这两点必然同在二分图的某一侧;现在添加了 \((u, v)\) 之间的连边,又意味着 \((u, v)\) 一定在二分图的两侧,产生了矛盾。故此时就可以无需处理后面还没有处理的连边,直接判定该图不是二分图。若处理完了所有连边且未产生矛盾,则可以判定该图是二分图。

posted @ 2026-01-27 20:54  小橘奏  阅读(41)  评论(0)    收藏  举报