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

题意
给出一组数表示每个节点的父节点,要修改最少的边使得这些节点变成一棵树
思路
根据题意,不符合要求的有两种可能,图中有环路,或者树根不止一个。
用并查集进行分类,将相连的节点划分到一个集合,随机指派一个符合要求的根节点(其父节点为自己)为最终的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;
}
发布了267 篇原创文章 · 获赞 12 · 访问量 15万+

猜你喜欢

转载自blog.csdn.net/u010734277/article/details/51980433