两种解法:
一、树状DP
1 /*by SilverN*/ 2 #include<iostream> 3 #include<algorithm> 4 #include<cstring> 5 #include<cstdio> 6 #include<cmath> 7 using namespace std; 8 const int INF=100000000; 9 const int mxn=1002000; 10 int hd[mxn],to[mxn],next[mxn]; 11 int f[mxn],g[mxn],c[mxn]; 12 int vis[mxn]; 13 int cnt=0; 14 int n,p,eg; 15 void add_edge(int u,int v){ 16 to[++cnt]=v;next[cnt]=hd[u];hd[u]=cnt; 17 return; 18 } 19 void dfs(int now){ 20 vis[now]=1; 21 if(vis[c[now]]) 22 p=now; 23 else dfs(c[now]); 24 return; 25 } 26 void solve(int now,int fa){ 27 f[now]=1;g[now]=INF;vis[now]=1; 28 if(now==eg){ 29 g[now]=0; 30 } 31 int u=hd[now]; 32 while(u!=0){ 33 if(to[u]!=fa && to[u]!=p) 34 { 35 // printf("test msg4: now (%d) to (%d) ",now,to[u]); 36 solve(to[u],now); 37 g[now]+=min(f[to[u]],g[to[u]]); 38 g[now]=min(g[now],f[now]+f[to[u]]-1); 39 f[now]+=min(f[to[u]],g[to[u]]); 40 // printf("test msg5: f[now]: %d g[now]: %d ",f[now],g[now]); 41 } 42 u=next[u]; 43 } 44 } 45 int main(){ 46 scanf("%d",&n); 47 int i,j; 48 for(i=1;i<=n;i++){ 49 scanf("%d",&c[i]); 50 add_edge(c[i],i);//反向存边 51 } 52 int ans=0; 53 for(i=1;i<=n;i++){ 54 if(!vis[i]){ 55 // printf("test msg1: dfs(%d) ",i); 56 dfs(i); 57 // printf("test msg2: p(%d) ",p); 58 eg=c[p]; 59 solve(p,0); 60 int tmp=f[p]; 61 // printf("test msg3: tmp(%d) ",tmp); 62 eg=0; 63 solve(p,0); 64 ans+=min(tmp,g[p]); 65 } 66 } 67 printf("%d ",n-ans); 68 }
二、强行拓扑贪心
AC
1 #include<iostream> 2 #include<algorithm> 3 #include<cstring> 4 #include<cstdio> 5 #include<cmath> 6 #include<queue> 7 using namespace std; 8 const int INF=100000000; 9 const int mxn=1002000; 10 int in[mxn]; 11 int ctl[mxn]; 12 bool flag[mxn]; 13 int n,cnt=0; 14 int ans=0; 15 int main(){ 16 scanf("%d",&n); 17 int i,j; 18 for(i=1;i<=n;i++){ 19 scanf("%d",&ctl[i]); 20 in[ctl[i]]++;//统计入度 21 } 22 queue<int>q; 23 for(i=1;i<=n;i++){ 24 if(!in[i]) q.push(i); 25 } 26 int tmp; 27 while(!q.empty()){ 28 tmp=q.front(); 29 q.pop(); 30 if(!flag[tmp] && !flag[ctl[tmp]]){ 31 ans++; 32 flag[ctl[tmp]]=1; 33 in[ctl[ctl[tmp]]]--; 34 if(!in[ctl[ctl[tmp]]]){//减后入度为0 35 q.push(ctl[ctl[tmp]]); 36 } 37 38 } 39 flag[tmp]=1; 40 } 41 for(i=1;i<=n;i++){ 42 if(!flag[i]){ 43 cnt=1;j=i; 44 flag[i]=1; 45 while(ctl[j]!=i){ 46 flag[ctl[j]]=1;//处理环 47 cnt++; 48 j=ctl[j]; 49 } 50 ans+=cnt/2;//环上一半的点可以投放 51 } 52 } 53 printf("%d ",ans); 54 return 0; 55 }