题目链接~~~~
许久没做并查集的题了,补一波。
题意:修复一棵树,给出一棵树的父节点,然后他可能存在环而变成了图,或者根节点不为1.
题目分析:
1.没有环的,则为树。
2.有环的,判断是否为1个根节点,如果一个根节点则拆环则将其他的环拆了插在这个根节点上。
#include <bits/stdc++.h>
using namespace std;
const int maxn = 200010;
int n, fa[maxn], a[maxn], ans[maxn];
int find(int v)
{
if(fa[v] == v) return v;
fa[v] = find(fa[v]);
return fa[v];
}
void unite(int x, int y)
{
int fx = find(x), fy = find(y);
if(fx != fy) fa[x] = y; // 注意一下这里,这题的根节点有讲究。
}
int main()
{
while(scanf("%d", &n) != EOF)
{
for(int i = 0; i <= n; i++) fa[i] = i;
int t = 0, cnt = 0;
for(int i = 1; i <= n; i++)
{
scanf("%d", &a[i]);
int fx = find(i), fy = find(a[i]);
if(fx == fy) // 判环
{
ans[cnt++] = i;
if(a[i] == i) t = fx; // 一个根节点
}
unite(fx, fy);
}
if(t == 0) // 找一个根结点
for(int i = 1; i <= n; i++)
{
int fx = find(i), fy = find(a[i]);
if(fx == fy)
t = fx;
}
int cnt2 = 0;
for(int i = 0; i < cnt; i++)
{
if(a[ans[i]] != t)
{
a[ans[i]] = t;
cnt2++;
}
}
printf("%d\n", cnt2);
for(int i = 1; i < n; i++)
printf("%d ", a[i]);
printf("%d\n", a[n]);
}
return 0;
}