题意
给定每个节点的父亲,判断是否为一棵有根树;若不是,改变最少的节点的父亲,使其成为有根树。(节点规模2*105)。
题目分析
比赛时一脸懵逼,后来看了大神代码——本题就是“见招拆招”:从叶子(尚未访问过的节点)往上找其最远的祖先,若能找到根,则合题;若找到环或找到与树不连通的祖先,则将祖先的父亲指向根。
代码中dep[i]代表i是第几批被访问的。
代码
#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<cmath>
#include<map>
#include<algorithm>
using namespace std;
const int maxn=2e5+5;
int a[maxn];
int root=0;
int dep[maxn]={0};
int cnt=0;
int ans;
void work(int x)
{
dep[x]=++cnt;
while(dep[a[x]]==0)
x=a[x],dep[x]=cnt;
if(dep[a[x]]==cnt)//祖先是当前批次,则有可能是环、不连通
{
if(root==0) root=x;
if(a[x]!=root)//祖先是环或不连通
a[x]=root,ans++;
}
}
int main()
{
int n;
cin>>n;
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
for(int i=1;i<=n;i++)
if(a[i]==i) root=i;
for(int i=1;i<=n;i++)
if(dep[i]==0) work(i);
cout<<ans<<endl;
for(int i=1;i<n;i++) printf("%d ",a[i]);cout<<a[n]<<endl;
return 0;
}