[CF505D] Mr. Kitayuta's Technology - 拓扑排序,并查集


[CF505D] Mr. Kitayuta's Technology - 拓扑排序,并查集

Description

在一个有向图中,有n个顶点,给出m对数字(u,v)表示顶点u和顶点v必须直接或者间接相连,让你构造一个这样的图,输出最少需要多少条边。

Solution

把题目给定的 (u,v) 当作有向边,下面说的连通块是将这些边视为无向的意义上的

对于一个大小为 x 的连通块,有环则代价为 x,否则代价为 x-1

#include 
using namespace std;

#define int long long
vector> g(1000000 + 2);
int id[1000005], idx, deg[1000005], fa[1000005], tag[1000005];

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

void merge(int x, int y)
{
    x = find(x);
    y = find(y);
    fa[x] = y;
}

void dfs(int p)
{
    id[p] = -1;
    for (int q : g[p])
    {
        if (id[q] == 0)
            dfs(q);
    }
    id[p] = ++idx;
}

signed main()
{
    ios::sync_with_stdio(false);

    int n, m;
    cin >> n >> m;

    for (int i = 1; i <= n; i++)
        fa[i] = i;

    for (int i = 1; i <= m; i++)
    {
        int u, v;
        cin >> u >> v;
        g[u].push_back(v);
        deg[v]++;
        merge(u, v);
    }

    for (int i = 1; i <= n; i++)
        if (deg[i] == 0 && id[i] == 0)
            dfs(i);

    for (int i = 1; i <= n; i++)
        if (id[i] == 0)
            tag[find(i)] = 1;

    for (int i = 1; i <= n; i++)
    {
        for (auto j : g[i])
            if (id[i] < id[j])
                tag[find(i)] = 1;
    }

    int ans = n;
    for (int i = 1; i <= n; i++)
        if (find(i) == i)
        {
            if (tag[i] == 0)
                ans--;
        }

    cout << ans << endl;
}

相关