CodeForces 699DFix a Tree

来源:互联网 发布:非诚勿扰程序员死亡 编辑:程序博客网 时间:2024/06/10 03:54

2016暑期集训3-F

CodeForces 699D Fix a Tree

并查集变形

传送门:CodeForce

传送门:HustOJ


题意

给你N个数a[i],表示i的父节点是a[i]。这构成了一片森林,注意可能有的树只有一个节点,有的树有环。要你修改尽可能少的a[i],使森林变成一棵树。


思路

维护关系首先想到并查集,但是这个并查集需要改,然而我的代码处理环的时候可能不太好。问了正雄大佬后,明白了思路就是读入的时候,如果i=a[i],那么把这个节点作为root,如果有多个这样的节点,把最后一个作为root。第二遍遍历的时候用并查集,如果i和a[i]的父亲不一样,并查集把i指向a[i];如果一样,说明它自成一棵树,把a[i]改成root。如果root=0,说明第一遍遍历的时候没有i=a[i]的点,说明没有自成一颗树的点,而且一定有环。 那么第二遍遍历时到第一个环的最后的时候,并查集会发现i和a[i]属于同一集合,因为在前面已经合并过环的其他部分了。就把这个i当成root,以后所有环到无法合并的时候都指向root就行了。想不明白举一个两个环的样例走一遍就行了。

代码

#include <iostream>#include<cstdio>#include<algorithm>#include<cstdlib>#include<cstring>#include<cmath>#include<vector>#include<queue>#include<stack>#include<iomanip>#include<string>#include<map>using namespace std;typedef long long int ll;const int MAXN = 200007;const int oo = 2000000007;const long long int loo = 2000000000000000007ll;int father [ MAXN ];void init ( int n ){    for ( int i = 1; i <= n; i++ )    {        father [ i ] = i;    }}int find ( int x ){    if ( father [ x ] == x ) return x;    else    {        return father [ x ] = find ( father [ x ] );    }}int main ( ){    int n;    int res = 0;    scanf ( "%d" , &n );    init ( n );    int t [ MAXN ];    memset ( t , 0 , sizeof ( t ) );    int root = 0;    for ( int i = 1; i <= n; i++ )    {        scanf ( "%d" , &t [ i ] );        if ( i == t [ i ] )        {            root = i;        }    }    for ( int i = 1; i <= n; i++ )    {        if ( i == root ) continue;        int fa = find ( i ) , fb = find ( t [ i ] ) ;        if ( fa == fb )        {            if ( root == 0 ) root = i;            t [ i ] = root;            res++;        }        else father [ fa ] = fb;    }    printf ( "%d\n" , res );    for ( int i = 1; i <= n; i++ )    {        printf ( "%d%c" , t [ i ] , i == n ? '\n' : ' ' );    }    //system ( "pause" );    return 0;}
0 0
原创粉丝点击