思路:首先,怎么用并查集判断时候有环?你在合并两个节点的时候,如果他们之前已经处在同一个连通分量里,那么把这两个节点合并的时候必定会产生环。
怎么建树?如果存在父节点指向自己的点,那么就把它作为这棵树的根。如果不存在,那么随便开一个环,把开环的节点作为根节点。
并查集的过程可以同时完成建树和查环的操作,如果合并这两个节点会形成环,那就开环后把一个节点指向根节点。如果不会,那就把他们合并。
#include <cmath>
#include <cstring>
#include <cstdio>
#include <vector>
#include <string>
#include <algorithm>
#include <string>
#include <map>
#include <set>
using namespace std;
#define MAXN 200010
#define LEN 1000000
#define INF 1e9+7
#define MODE 1000000
typedef long long ll;
int par[MAXN];
void init(int n)
{
for(int i=1;i<=n;i++){
par[i]=i;
}
}
int find(int x){
if(par[x]==x)
return x;
else
return par[x]=find(par[x]);
}
void unite(int x,int y)
{
x=find(x);
y=find(y);
if(x==y)
return;
else
par[x]=y;
}
bool same(int x,int y)
{
return find(x)==find(y);
}
struct edge{
int u,v;
};
vector <edge> e;
int main()
{
int n;
while(scanf("%d",&n)!=EOF)
{
init(n);
int ans;
int root=-1;
for(int i=1;i<=n;i++)
{
edge temp;
temp.u=i;
scanf("%d",&temp.v);
e.push_back(temp);
if(temp.u==temp.v)
root=i;
}
int res=0;
for(int i=0;i<e.size();i++)
{
if(e[i].u!=root)
{
if(same(e[i].u,e[i].v))
{
res++;
if(root==-1)
root=e[i].u;
e[i].v=root;
}
else
unite(e[i].u,e[i].v);
}
}
printf("%d\n",res);
for(int i=0;i<e.size();i++)
{
printf("%d",e[i].v);
if(i!=e.size()-1)
printf(" ");
}
printf("\n");
}
}