并查集的裸题
我们不难发现,若存在一些小猪比如说1->2,2->3,3->4那么我们只需要打开4,那么之前的1,2,3都能被打开,所以我们可以将钥匙看成两头猪之间的边,将两头猪合并到一个并查集里,那么这个并查集只要有一个猪被打开 ,那么整个联通块就都能打开了,所以说我们可以边读入边合并,最后数一下联通块个数就好,据说还有tarjan做法,也是统计联通块个数233,缩点之后统计有多少个入度为0的联通分量就好,我用的并查集,代码少啊233
代码
//By AcerMo
#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
const int M=1e6+50;
int n,fa[M],siz[M];
inline int find(int x)
{
if (x!=fa[x]) return fa[x]=find(fa[x]);
return x;
}
inline int read()
{
int x=0;char ch=getchar();
while (ch>'9'||ch<'0') ch=getchar();
while (ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
return x;
}
inline void unionn(int a,int b)
{
if (siz[a]<=siz[b]) siz[b]+=siz[a],fa[a]=b;
else siz[a]+=siz[b],fa[b]=a;
return ;
}
signed main()
{
n=read();int ans=n;
for (int i=1;i<=n;i++)
fa[i]=i,siz[i]=1;
for (int i=1;i<=n;i++)
{
int a=read();
int r1=find(i),r2=find(a);
if (r1!=r2) unionn(r1,r2),ans--;
}
cout<<ans;
return 0;
}