【codeforces 698B】 Fix a Tree

题目链接:

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

题解:

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

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

 

 1 #include<cstdio>
 2 inline int min(int a,int b){return a<b?a:b;}
 3 inline int read(){
 4     int s=0;char ch=getchar();
 5     while(ch<'0'||ch>'9')   ch=getchar();
 6     while(ch>='0'&&ch<='9') s=s*10+(ch^48),ch=getchar();
 7     return s;
 8 }
 9 const int N=200200;
10 int n;
11 int p[N];
12 int dfn[N],low[N],dfx,stk[N],top,scc;
13 bool instk[N];
14 int size[N];
15 int first[N];
16 inline void tarjin(int x){
17     dfn[x]=low[x]=++dfx;
18     stk[++top]=x;instk[x]=true;
19     if(!dfn[p[x]]){
20         tarjin(p[x]);
21         low[x]=min(low[x],low[p[x]]);
22     }else   if(instk[p[x]]) low[x]=min(low[x],dfn[p[x]]);
23     if(low[x]==dfn[x]){
24         int t;scc++;
25         do{
26             t=stk[top--];
27             size[scc]++;
28             if(!first[scc])
29                 first[scc]=t;
30             instk[t]=false;
31         }while(t!=x);
32     }
33 }
34 int main(){
35     n=read();
36     for(int i=1;i<=n;i++){
37         p[i]=read();
38     }
39     int ans=0;
40     int to=0; 
41     for(int i=1;i<=n;i++)
42         if(p[i]==i){ ans--;break;}
43     for(int i=1;i<=n;i++){
44         if(!dfn[i])
45             tarjin(i);
46         if(p[i]==i&&!to)
47             to=i;
48     }
49     if(!to){
50         to=p[first[1]]=first[1];
51     }
52     for(int i=1;i<=scc;i++){ 
53         if(size[i]>1||(first[i]==p[first[i]]&&size[i]==1)){
54             p[first[i]]=to;
55             ans++;
56         }
57     }
58     printf("%d\n",ans);
59     for(int i=1;i<=n;i++)
60         printf("%d%c",p[i],i<n?' ':'\n');
61 }

 

 

 

posted @ 2017-09-02 11:53  Troywar  阅读(300)  评论(0编辑  收藏  举报