题解:P2661 [NOIP2015 提高组] 信息传递
P2661 [NOIP2015 提高组] 信息传递
刚学并查集,花了挺久才想清楚这题为什么能用并查集,写个题解整理思路。
题目大意
给定一个
保证每个点有且仅有
思路分析
其实这题完全没必要用并查集,由于每个点出度都为
样例的图如下:
不难确定答案是
首先建立并查集并初始化,开始每个点各自属于一个集合。同时我们要设一个数组
(3,4 放反了)。
然后我们添加
接着我们添加
(使用路径压缩后,
然后我们要添加
但我们要如何确定最小环的长度呢?这时候
inline int find(int x) {
if (father[x] != x) {
int lst = father[x];
father[x] = find(father[x]);
v[x] += v[lst];
}
return father[x];
}
inline void merge(int x,int y) {
int p = find(x);
int q = find(y);
if (p != q) {
father[p] = q;
v[x] = v[y] + 1;
}else{
ans = min(ans,v[x]+v[y]+1);
}
}
int main () {
cin >> n;
for (int i = 1; i <= n; i++) father[i] = i;
for (int i = 1; i <= n; i++) {
int t;
cin >> t;
merge(i,t);
}
cout << ans << '\n';
return 0;
}
当我们添加一条边时,如果这条边连接的两个点
为什么只更新
需要注意的是,这种方法只适用于所有点出度仅有
可能以上说的有些琐碎,建议自己手玩一下样例可能就清楚了。
AC Code
#include <bits/stdc++.h>
using namespace std;
int n,ans = 0x3f3f3f3f;
int father[200005],v[200005],cnt;
inline int find(int x) {
if (father[x] != x) {
int lst = father[x];
father[x] = find(father[x]);
v[x] += v[lst];
}
return father[x];
}
inline void merge(int x,int y) {
int p = find(x);
int q = find(y);
if (p != q) {
father[p] = q;
v[x] = v[y] + 1;
}else{
ans = min(ans,v[x]+v[y]+1);
}
}
int main () {
cin >> n;
for (int i = 1; i <= n; i++) father[i] = i;
for (int i = 1; i <= n; i++) {
int t;
cin >> t;
merge(i,t);
}
cout << ans << '\n';
return 0;
}