您的位置:首页 > 其它

Codeforces 698B Fix a Tree 并查集

2016-07-25 15:16 441 查看
题目链接:http://www.codeforces.com/problemset/problem/698/B

题意:

    已知有向树的定义,除了根节点外每个节点都有且仅有一条边都指向它的父亲节点,而根节点有且仅有一条边指向自己。

    现在输入一张图,每个节点都有一条边指向某个点,要求修改尽量少的边,将这张图变成一颗树。

分析;

    最终形成的树中一定只有根节点指向自己,并且除了根节点的自环外没有其他的环。

    对于图中形成的一个环,我们只需要拆开一个点就能将这个环修改成一颗树。并且由于每个点只有一个父亲节点,所以原来的图中绝对不会出现两个环相连的情况。

    那么只需要设定一个根节点,将其他所有的环都拆开并且指向这个根节点就可以了。

    根节点设定的时候优先选取原先已经满足根节点条件的点,即其父亲为自己的点。如果这样的点不存在,任意选取一个点即可。

   

    具体的操作:首先用并查集统计出所有的环的数量,并对每个环任意设定一个小的根节点。之后设定好真正的根节点然后将所有的小根节点都指向这个根节点就行了。

/***********************************************
|Author: Fry
|Created Time: 2016/7/25 14:47:15
|File Name: 698B.cpp
|Copyright:
|  For personal use, feel free to use
|  Otherwise call me at http://blog.csdn.net/fry_guest ***********************************************/
#include <bits/stdc++.h>
using namespace std;
const int N=2e5+5;
int a
,f
,check
;
int num;
int find(int x)
{
if (check[x]==num) return f[x]=x;
check[x]=num;
if (f[x]==x) return x;
return f[x]=find(f[x]);
}
int main()
{
int n,ans,x;
while (~scanf("%d",&n)){
memset(check,0,sizeof(check));
for (int i=1;i<=n;i++) scanf("%d",&a[i]);
for (int i=1;i<=n;i++) f[i]=a[i];
for (int i=1;i<=n;i++){
num=i;
find(i);
}
ans=0;
x=0;
for (int i=1;i<=n;i++){
if (f[i]==i&&a[i]==i){
x=i;
break;
}
}
if (!x){
for (int i=1;i<=n;i++){
if (f[i]==i){
x=i;
a[i]=i;
ans++;
break;
}
}
}
for (int i=1;i<=n;i++){
if (i!=x&&f[i]==i) {
a[i]=x;
ans++;
}
}
printf("%d\n",ans);
for (int i=1;i<n;i++) {
printf("%d ",a[i]);
}printf("%d\n",a
);
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  并查集 codeforces