您的位置:首页 > 其它

Codeforces Round #363 (Div. 2) D. Fix a Tree

2016-07-21 10:45 260 查看
题意
给出一组数表示每个节点的父节点,要修改最少的边使得这些节点变成一棵树

思路
根据题意,不符合要求的有两种可能,图中有环路,或者树根不止一个。

用并查集进行分类,将相连的节点划分到一个集合,随机指派一个符合要求的根节点(其父节点为自己)为最终的root,将其他集合若没有环路,则直接将其根节点指向root。若有环,则在环中找一个节点将其父节点修改为root。寻找环的方法:如果其父节点不是自己但是其所在集合的代表元素是它,那么就修改它的父节点就好了。

代码如下

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;

int n;
int fa[200005];
int p[200005];

int find(int v){
return fa[v] = fa[v] == v ? v : find(fa[v]);
}

void merge(int a, int b)
{
int x = find(a); int y = find(b);
fa[x] = y;
}

int main()
{
//    freopen("data.txt","r",stdin);
scanf("%d",&n);
int ans = 0;
int root = -1;
for(int i = 1; i <= n; ++i){
scanf("%d",&p[i]);
fa[i] = i;
if(p[i] == i && root == -1)root = i;
}

for(int i = 1; i <= n; ++i){
merge(i,p[i]);
}

for(int i = 1; i <= n; ++i){
if(root == -1 && p[i] == i){
root = i;
} else if( i == find(i) && p[i] != i){
ans ++;
p[i] = root == -1 ? i : root;
if(p[i] == i)root = i;
merge(i,root);
} else if(p[i] == i && i != root){
ans++;
p[i] = root;
merge(i,root);
}
}

printf("%d\n",ans);
printf("%d",p[1]);
for(int i = 2; i <= n; ++i){
printf(" %d",p[i]);
}
puts("");
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: