Description
给你一张有向图每个点的唯一入边,求最少更改多少条入边才能让图是一棵树,根节点的入边为自己。
Solution
给定的图由若干个联通分量构成,其中有的联通分量为简单环,有的为树。
若全为环,则让任意一个环中任意一点的边改连自己,作为根节点,其他的环任选一个点连过来。若有至少一棵树,则将其中一棵的根节点作为总的根节点,其它的环任选一个点连过来,其它的树把根节点连过来。
可以发现,有 个联通分量,就要有 个联通分量连向总的根节点,如果全为环,则需要额外一次操作将一个环变成一棵树。
所以需要一个数据结构,能够求联通分量数还能判环,用并查集实现。如果一条边的两个节点在一个集合中,因为这两个点已经联通,而这两个点又有了一条边且不为重边,所以有环。
Code
#include <bits/stdc++.h>
using namespace std;
const int N = 2e5 + 5;
int read() {
int x = 0, f = 0; char ch = 0;
while (!isdigit(ch)) f |= ch == '-', ch = getchar();
while (isdigit(ch)) x = (x << 3) + (x << 1) + (ch ^ 48), ch = getchar();
return f ? -x : x;
}
int f[N], fa[N];
int find(int k) {
if (f[k] == k) return k;
return f[k] = find(f[k]);
}
int main(){
int n = read();
for (int i = 1; i <= n; i++) {
fa[i] = read(); f[i] = i;
}
int ans = 0;
bool flg = 0;
for (int i = 1; i <= n; i++) {
int x = find(i), y = find(fa[i]);
if (x == y) ans++;
if (fa[i] == i) flg = 1;
f[x] = y;
}
printf("%d\n", ans - flg);
return 0;
}