【学习记录】并查集

并查集是一个实现起来较为简单的数据结构。它维护的是一系列不相交集合(当然方便的说法是一堆点),每一个集合都由该集合中的一个代表元表示。但它可以有奇妙的用法。

基本实现

整个并查集实际上是一个森林,每一个元素 x x 都有一个属性 f a [ x ] fa[x] 作为 x x 所在的树的父节点。树根的父节点设为它本身。这样只要不断沿着 f a [ x ] fa[x] 走就能走到这一棵树的根,从而知道一个元素的归属。

事实上整棵树的形态并不重要,因为我们需要知道的是一个元素所在树的根节点。因此可以在递归找根时,令经过路径上的节点的 f a fa 全部设为最后找到的根,这样可以方便之后的查询。这种优化称为路径压缩。路径压缩后的并查集查找操作的均摊时间复杂度是 O ( log n ) O(\log n)

并查集还要支持合并两个集合的操作。这个操作的基本实现方式是找出两个集合对应的树根,然后令其中一个根的 f a fa 设为另外一个树根。这时的合并是可以指定方向的,即指定新集合的代表元是哪一个根。

如果不需要指定代表元就可以使用按秩合并。它是一种启发式合并。它将秩(一般定义为集合大小或者合并次数)较小的集合合并到秩较大的集合上,这样就只增加了小集合的查询代价。这样总共增加的总代价是 O ( n log n ) O(n\log n) 级别的。这时查询操作的均摊时间复杂度也是 O ( log n ) O(\log n)

两种优化都使用的并查集的查询时间复杂度近似于常数级别。

int fa[100005], rnk[100005], n;
void init(){
    for (int i = 1; i <= n; ++i)
        fa[i] = i, rnk[i] = 1;
}
int Find(int x){
    return (x == fa[x] ? x: (fa[x] = Find(fa[x])));
}
int Union(int x, int y){
    x = Find(x), y = Find(y);
    if (x == y) return 0;
    if (rnk[x] > rnk[y]) fa[y] = x, rnk[x] += rnk[y];
    else fa[x] = y, rnk[y] += rnk[x];
    return 1;
}

基于并查集树的性质,可以将并查集的树边视作有向边或者无向边,这两种视角可以分别处理不同的问题。

无向边的并查集常用于维护无向图的连通性。这在 Kruskal 算法中得到了体现。扩展一下:并查集擅长维护等价关系(集合即等价类)。

有向边的并查集常用于维护一个数组中空闲和占用的情况。比如,可以令一个集合的根表示当前可用位置,如果占用了这个位置就把该位置所在集合和该位置之前的一个位置所在集合合并,这么做就可以快速查询一个位置之前最近的空闲位置。

有向边的并查集也可以用来维护链状或者树形的关系,这时可以对每一个元素维护一些额外的信息。

带权并查集

并查集的边可以拥有边权,而边权可以表示一些信息。

带权并查集和普通并查集主要在两个地方有所不同:一是在查找操作中,路径压缩时要进行边权的更新;二是在合并操作中,要确定合并方向,以及合并中新产生的边的边权。根据不同的问题会有不同的处理方法。重点在于弄清楚边权的意义。

int fa[100005], dis[100005], n;
int Find(int x){
    if (x == fa[x]) return x;
    int rt = Find(fa[x]);
    // 更新边权操作
    return (fa[x] = rt);
}
void joint(int from, int to){
    from = Find(from), to = Find(to);
    if (from == to) return ;
    fa[from] = to;
    // 更新dis[xa]
}

典型问题如 NOI2002 银河英雄传说。

多重关系并查集

联想到并查集能够维护一系列等价关系,我们可以将一个点拆成多个点,每一个点表示与原对象相关的某个命题,如和 x 相同或者和 x 不同。这样如果题目中给出的都是双蕴含(或者异或)类型的命题,就可以利用这种并查集高效地维护命题之间的关系。

典型问题如 NOI2001 食物链。每一个动物都拆成三个点,表示其为 A 族、其为 B 族、其为 C 族三个命题。

  • 如果 X 和 Y 同族,那么就把 X 是 P 族和 Y 是 P 族,P 取 A、B、C 这三对命题连起来。
  • 如果 X 吃 Y,那么就把 X 是 A 族和 Y 是 B 族、X 是 B 族和 Y 是 C 族、X 是 C 族和 Y 是 A 族这三对命题连起来。

猜你喜欢

转载自blog.csdn.net/zqy1018/article/details/108293531