Operating on a Graph(并查集,图)

132 阅读2分钟

Operating on a Graph (nowcoder.com)

题目描述

给你一张有 nn 个节点和 mm 条边的无向图,nn 个节点从 00 编号到 n1n - 1,每个节点 ii 最初属于组织 ii。接下来给出 qq 次操作,每次操作为一个组织的编号 oo,若这个组织已经没有成员则无事发生,否则对于与这个组织所有成员有连边的组织的所有成员,这些成员所属的组织更改为 oo

qq 次操作后,从 00n1n-1 输出每个成员的最终所属组织。

共有 tt 组样例。

题目分析

首先对于每个成员其所属的组织,我们可以用一个数组来维护,并通过并查集的方式进行更改。

让我们模拟这个过程,首先给出组织编号 oo,我们先通过并查集寻找的方式判断组织 oo 是否存在(其实这步可以省去,因为若当前组织不存在则其边没有所连组织,不会对后续操作造成干扰)。若存在,则对于这个组织向外所连的组织,我们需要将这些组织所有成员的组织编号以并查集的方式更改为 oo,同时需要将其所连接的其他组织归为组织 oo 所直接相连的组织。这个操作我们可以用容器 listlist 来实现,对于更改相应组织所连接的其他组织的所连组织,我们可以用函数 splice()splice() 实现,整个过程类似于 bfsbfs 的方式,即层层递进向外拓展,注意在每次拓展的过程中需要判断所连组织的所连组织是否是初始组织本身。

整个过程的时间复杂度为 O(n+m)O(n+m)

Accept代码

#include <bits/stdc++.h>

using namespace std;

const int N = 8e5 + 10;
int p[N];
list<int> g[N];

int find(int x)
{
    return x == p[x] ? p[x] : p[x] = find(p[x]);
}

int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    
    int t; cin >> t;
    while (t --)
    {
        int n, m;
        cin >> n >> m;
        for (int i = 0; i <= n; i ++) p[i] = i, g[i].clear();
        while (m --)
        {
            int a, b;
            cin >> a >> b;
            g[a].push_back(b);
            g[b].push_back(a);
        }
        int q; cin >> q;
        while (q --)
        {
            int x; cin >> x;
            if (x == find(x))
            {
                list<int> s;
                for (auto y : g[x])
                {
                    y = find(y);
                    if (y != x) p[y] = x, s.splice(s.end(), g[y]);
                }
                swap(g[x], s);
            }
        }
        for (int i = 0; i < n; i ++) cout << find(i) << "\n "[i < n - 1];
    }
    return 0;
}