Description
Byteazar 有 N 个小猪存钱罐. 每个存钱罐只能用钥匙打开或者砸开. Byteazar 已经把每个存钱罐的钥匙放到了某些存钱罐里. Byteazar 现在想买一台汽车于是要把所有的钱都取出来. 他想尽量少的打破存钱罐取出所有的钱,问最少要打破多少个存钱罐.
Input
第一行一个整数 N (1 <= N <= 1.000.000) – 表示存钱罐的总数. 接下来每行一个整数,第 i+1行的整数代表第i个存钱罐的钥匙放置的存钱罐编号.
Output
一个整数表示最少打破多少个存钱罐.
Sample Input
4
2
1
2
4
2
1
2
4
Sample Output
2
In the foregoing example piggy banks 1 and 4 have to be smashed.
这题简直卧槽。把tarjan做法的内存给卡了一下
难道这题第一眼不是tarjan么= =为啥会想到并查集。。
我们tarjan缩点后把所有入度为0的点统计一下就OK了
【tarjan的dfn数组原来可以削掉。。】
#include<cstdio>
#include<string>
#include<cstring>
#include<algorithm>
using namespace std;
int n;
struct map
{
int t;
int next;
}a[1000001];
int edge;
int head[1000001];
int scc,cnt;
bool v[1000001];
int s[1000001],top;
int low[1000001],belong[1000001];
inline void add(int s,int t)
{
a[edge].next=head[s];
head[s]=edge;
a[edge].t=t;
}
inline int min(int x,int y)
{
if(x<y)
return x;
return y;
}
void tarjan(int d)
{
cnt++;
int dfn=cnt;
low[d]=cnt;
top++;
s[top]=d;
v[d]=true;
for(int i=head[d];i!=0;i=a[i].next)
{
if(low[a[i].t]==0)
{
tarjan(a[i].t);
low[d]=min(low[d],low[a[i].t]);
}
else if(v[a[i].t])//v在栈中,修改low[u]
low[d]=min(low[d],low[a[i].t]);
}
if(dfn==low[d])//u为该强连通分量中遍历所成树的根
{
scc++;
int x=s[top];
top--;
while(x!=d)
{
v[x]=false;
belong[x]=scc;
x=s[top];
top--;
}
v[x]=false;
belong[x]=scc;
}
}
inline void count_edge()
{
int i,j,d;
for(i=1;i<=n;i++)
{
for(j=head[i];j!=0;j=a[j].next)
{
d=a[j].t;
if(belong[i]!=belong[d])
{
v[belong[d]]=true;
}
}
}
}
int main()
{
scanf("%d",&n);
int i,x;
for(i=1;i<=n;i++)
{
scanf("%d",&x);
edge++;
add(x,i);
}
for(i=1;i<=n;i++)
if(low[i]==0)
tarjan(i);
int ans=0;
memset(v,false,sizeof(v));
count_edge();
for(i=1;i<=scc;i++)
if(!v[i])
ans++;
printf("%d\n",ans);
return 0;
}