[CF505D] Mr. Kitayuta's Technology - 拓扑排序,并查集
Description
在一个有向图中,有n个顶点,给出m对数字(u,v)表示顶点u和顶点v必须直接或者间接相连,让你构造一个这样的图,输出最少需要多少条边。
Solution
把题目给定的 (u,v) 当作有向边,下面说的连通块是将这些边视为无向的意义上的
对于一个大小为 x 的连通块,有环则代价为 x,否则代价为 x-1
#include <bits/stdc++.h>
using namespace std;
#define int long long
vector<vector<int>> 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;
}