题目描述
杀人游戏是一个在信息奥赛选手中流行的游戏,这个游戏不在结果,重在参与,就像信息奥赛一样。这个游戏的一方称为杀手,剩下的一方称为平民。杀手知道每个人的身份,但是平民不知道。在游戏过程中,平民的任务是找出谁是杀手。
游戏玩了若干轮,现在还剩下n个人,每个人都指认了一个杀手。,当然,平民基本是乱猜的,而杀手则全部指认的是平民。在不知道谁是杀手的情况下,最多可能有多少杀手。
输入
输入格式:
第一行包含一个整数N(2<=N<=500000),表示还有n个人。这n个人标号为1到n。
接下来有n行,每行一个数,其中的第k行表示被第k个人指认为杀手的人。
没有谁会指认自己为杀手。
输出
一个整数,表示最多可能的杀手的数量。
样例输入
3
2
1
1
样例输出
2
一个伪森林。由于一个点只能指向另一个点,所以一个连通分量里最多有一个环。
有一个贪心算法,对于每个连通分量,找到所有入度为0的点,视为杀手,它们所指向的点就为平民。
删掉杀手点和平民点,其他点的入度相应改变。再重复这个操作多次。
最后只会剩下一个环。将环中的任一点视为平民(这并不影响),然后删掉这个点,环变成链,继续上面的操作就可以了。
#include<cstdio>
#define MAXN 500000
struct node{
int v;
node *next;
}edge[MAXN+10],*adj[MAXN+10],*ecnt=&edge[0];
int n,inp[MAXN+10],son[MAXN+10],ans;
bool vis[MAXN+10];
void read()
{
int x;
scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%d",&x);
son[i]=x;
inp[x]++;
}
}
void dfs(int u,int d)
{
if(vis[u]) return ;
vis[u]=true;
ans+=d;
if(--inp[son[u]]==0 || d==1)
dfs(son[u],1-d);
}
void workout()
{
for(int i=1;i<=n;i++)
if(!inp[i])
dfs(i,1);
for(int i=1;i<=n;i++)
dfs(i,0);
printf("%d\n",ans);
}
int main()
{
read();
workout();
return 0;
}