ZROI 普专提 树

Description

给你一张有向图每个点的唯一入边,求最少更改多少条入边才能让图是一棵树,根节点的入边为自己。

1 n 2 × 1 0 5 1 \leq n \leq 2 \times 10^5

Solution

给定的图由若干个联通分量构成,其中有的联通分量为简单环,有的为树。

若全为环,则让任意一个环中任意一点的边改连自己,作为根节点,其他的环任选一个点连过来。若有至少一棵树,则将其中一棵的根节点作为总的根节点,其它的环任选一个点连过来,其它的树把根节点连过来。

可以发现,有 x x 个联通分量,就要有 x 1 x-1 个联通分量连向总的根节点,如果全为环,则需要额外一次操作将一个环变成一棵树。

所以需要一个数据结构,能够求联通分量数还能判环,用并查集实现。如果一条边的两个节点在一个集合中,因为这两个点已经联通,而这两个点又有了一条边且不为重边,所以有环。

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;
}
发布了28 篇原创文章 · 获赞 38 · 访问量 492

猜你喜欢

转载自blog.csdn.net/qq_39984146/article/details/104223620