【codeforces 698B】 Fix a Tree

时间:2021-05-06 12:20:56

题目链接:

  http://codeforces.com/problemset/problem/698/B

题解:

  还是比较简单的。因为每个节点只有一个父亲,可以直接建反图,保证出现的环中只有一条路径。

  然后发现,有多少个环,就需要改多少条边。然后设出现连向自己的节点为x,这些也要改边,对答案的贡献应为:$max(x-1,0)$。对于最后的根节点,有自环选一个,没自环在其他环上任选一个点就行。

 #include<cstdio>
inline int min(int a,int b){return a<b?a:b;}
inline int read(){
int s=;char ch=getchar();
while(ch<''||ch>'') ch=getchar();
while(ch>=''&&ch<='') s=s*+(ch^),ch=getchar();
return s;
}
const int N=;
int n;
int p[N];
int dfn[N],low[N],dfx,stk[N],top,scc;
bool instk[N];
int size[N];
int first[N];
inline void tarjin(int x){
dfn[x]=low[x]=++dfx;
stk[++top]=x;instk[x]=true;
if(!dfn[p[x]]){
tarjin(p[x]);
low[x]=min(low[x],low[p[x]]);
}else if(instk[p[x]]) low[x]=min(low[x],dfn[p[x]]);
if(low[x]==dfn[x]){
int t;scc++;
do{
t=stk[top--];
size[scc]++;
if(!first[scc])
first[scc]=t;
instk[t]=false;
}while(t!=x);
}
}
int main(){
n=read();
for(int i=;i<=n;i++){
p[i]=read();
}
int ans=;
int to=;
for(int i=;i<=n;i++)
if(p[i]==i){ ans--;break;}
for(int i=;i<=n;i++){
if(!dfn[i])
tarjin(i);
if(p[i]==i&&!to)
to=i;
}
if(!to){
to=p[first[]]=first[];
}
for(int i=;i<=scc;i++){
if(size[i]>||(first[i]==p[first[i]]&&size[i]==)){
p[first[i]]=to;
ans++;
}
}
printf("%d\n",ans);
for(int i=;i<=n;i++)
printf("%d%c",p[i],i<n?' ':'\n');
}