并查集判环+思维 Codeforces Round #363 (Div. 2) D题 Fix a Tree

Fix a Tree

A tree is an undirected connected graph without cycles.

Let’s consider a rooted undirected tree with n vertices, numbered 1 through n. There are many ways to represent such a tree. One way is to create an array with n integers p1, p2, …, pn, where pi denotes a parent of vertex i (here, for convenience a root is considered its own parent).

在这里插入图片描述
Given a sequence p1, p2, …, pn, one is able to restore a tree:

  1. There must be exactly one index r that pr = r. A vertex r is a root of the tree.
  2. For all other n - 1 vertices i, there is an edge between vertex i and vertex pi.

A sequence p1, p2, …, pn is called valid if the described procedure generates some (any) rooted tree. For example, for n = 3 sequences (1,2,2), (2,3,1) and (2,1,3) are not valid.

You are given a sequence a1, a2, …, an, not necessarily valid. Your task is to change the minimum number of elements, in order to get a valid sequence. Print the minimum number of changes and an example of a valid sequence after that number of changes. If there are many valid sequences achievable in the minimum number of changes, print any of them.


题目大意:给你一个大小为n的序列 p[],p[i] 代表结点 i 的父亲是 p[i];p[i]==i 表示 i 是树的根结点,因为给的 p[] 不一定是一颗树,也不一定连通,如果要让这 n 个结点连成一颗树,最少更改父节点的次数;

分析题目可知,每个结点只有一个父节点,也就是说每个点的入度都为1,所以p[]序列要不就是树,要不就是环,可能是树和环的混合,因为图不一定连通;

这题就可以先找到一个根,并且记录每个环,把这些环拆开连接在这个根上面,就是一颗完整的n个结点的树了;

还要注意不一定能找到根,这时就要从环里面定义一个根,特判一下;

代码:

#include<bits/stdc++.h>
#define LL long long
#define pa pair<int,int>
#define ls k<<1
#define rs k<<1|1
#define inf 0x3f3f3f3f
using namespace std;
const int N=200100;
const int M=2000100;
const LL mod=1e9+7;
int n,p[N],fa[N],rt,loop[N],cnt;
int find(int p){
	if(p==fa[p]) return p;
	return fa[p]=find(fa[p]);
} 
int main(){
	scanf("%d",&n);
	for(int i=1;i<=n;i++) scanf("%d",&p[i]),fa[i]=i;
	for(int i=1;i<=n;i++){
		int fx=find(i),fy=find(p[i]);
		if(fx==fy){
			if(i==p[i]) rt=p[i];
			loop[++cnt]=i;//环
		}
		else fa[find(fx)]=find(fy);
	}
	int ans=0;
	if(!rt){
		for(int i=1;i<=n;i++) if(find(i)==i) rt=i;
		for(int i=1;i<=cnt;i++){
			ans++;
			p[loop[i]]=rt;
		}
	}
	else{
		for(int i=1;i<=cnt;i++){
			if(loop[i]!=rt){
				ans++;
				p[loop[i]]=rt;
			}
		}	
	}
	printf("%d\n",ans);
	for(int i=1;i<=n;i++) printf("%d ",p[i]);
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值