并查集。
如果a的钥匙保存在b中,b的钥匙保存在c中,那么实际上只需要打开c就可以打开后面的a和b了,所以说c是a和b的祖先。根据上述的原理,将所有的盒子分组,最后查看哟多少种不同祖先就可以了。在程序开始的时候,每个盒子是自己的祖先。在并查集的操作中,每次合并就减少了一种祖先。最后直接输出答案即可。
#include<iostream>
#include<memory.h>
using namespace std;
int num[1000010];
int rank[1000010];
int final[1000010];
int ans;
int findset(int x)
{
if(x!=num[x])
{
num[x]=findset(num[x]);//zhe li yao shi num[x]
}
return num[x];
}
void makeunion(int x,int y)
{
int fx=findset(x);
int fy=findset(y);
if(fx==fy)
return ;
else if(rank[fx]>rank[fy])
{
num[fy]=fx;
}
else
{
if(rank[fx]==rank[fy])
{
rank[fy]++;
}
num[fx]=fy;
}
ans--;
}
int main()
{
int n;
while(cin>>n)
{
memset(num,0,sizeof(num));
memset(rank,0,sizeof(rank));
memset(final,0,sizeof(final));
ans=n;
for(int i=1;i<=n;i++)
{
if(num[i]==0)
num[i]=i;
int tem;
cin>>tem;
if(num[tem]==0)
num[tem]=tem;
makeunion(i,tem);
}
cout<<ans<<endl;
}
}