题目链接:
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 }
时间: 2024-10-20 16:56:05