目录
题目描述
有很多信息学选手在外出比赛时,在宿舍里都会玩一种“杀人游戏”。本题的规则比正式的游戏简单很多。假设现在的游戏剩下N个人,这里的人分为两类:恶魔、平民。
恶魔是知道其他哪些人是恶魔哪些人是平民的,而平民则不知道其他哪些人士恶魔哪些人士平民。现在这N个人,每个人都要指控另一个人。一个恶魔一定是指控一个平民,而一个平民指控的人可能是恶魔也可能是平民。现在给出这N个人指控的关系,问满足这种指控关系的前提下,最多可以有多少个恶魔?
输入
第一行,一个整数N。有N个人,编号是从1至N。
接下来有N行,每行一个整数,第i行的整数ai,表示的意义是:第i个人指控第ai个人。没有人会指控自己。
输出
一个整数,表示最多可以有多少只恶魔
输入样例
样例3
2
1
1
样例2
3
2
3
1
样例3
7
3
3
4
5
6
4
4
输出样例
样例1
2
样例2
1
样例3
4
样例解释
第一个样例解释:杀手可能为2和3;
第二个样例解释:杀手可能是任何1个人,但不可能多于1个,否则杀手就会指认自己人了。
数据范围
40%的数据.
80%的数据
100%的数据
题解
分析一下,可以发现这个图不具有连通性。
可以发现图上的边的方向并没有卵用,因为坏人是不可以相互指认的,所以问题变成了选择一个点的集合,使得从这个集合出发的指认不指向这个集合中的任意元素,求集合的元素最多有多少个,如果这是一颗树的话就可以直接做最大独立集了。但是这不一定是一棵树。不过由于每个点只发出一条边可以证明这是一颗基环外向树。然后问题就简单了,找到环上的一条边,dp的时候不经过这条边(我用的dp),同时每一次dp之前要确定这条边的哪边是好人。然后就可以dp了。
再说明一个问题,代码中实际上如果这个环上只有两个点的话找不到这条边就算了,完全没有影响,充其量就是多dp了一次而已。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<cmath>
#include<queue>
#include<set>
#include<map>
#include<vector>
#include<cctype>
using namespace std;
typedef long long LL;
const int maxn=500005;
int N;
struct edge{ int to,next,id; }E[maxn<<1];
int first[maxn],np,ID,s,t;
int f[maxn][2],ans;
bool vis[maxn];
void read(int &x)
{
x=0;
char ch=getchar();
while(ch<'0'||ch>'9') ch=getchar();
while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
}
void add_edge(int u,int v,int id)
{
E[++np]=(edge){v,first[u],id};
first[u]=np;
}
void data_in()
{
read(N);
int x;
for(int i=1;i<=N;i++)
{
read(x);
add_edge(x,i,i);
add_edge(i,x,i);
}
}
void DFS(int i,int fa)
{
vis[i]=1;
for(int p=first[i];p;p=E[p].next)
{
int j=E[p].to;
if(vis[j])
{
if(j!=fa) ID=E[p].id,s=i,t=E[p].to;
continue;
}
DFS(j,i);
}
}
void DFS(int i,int fa,int nc)
{
f[i][0]=f[i][1]=0;
if(i!=nc) f[i][1]=1;
for(int p=first[i];p;p=E[p].next)
{
if(ID==E[p].id) continue;
int j=E[p].to;
if(j==fa) continue;
DFS(j,i,nc);
f[i][0]+=max(f[j][0],f[j][1]);
if(i!=nc) f[i][1]+=f[j][0];
}
}
void work()
{
for(int i=1;i<=N;i++) if(!vis[i])
{
DFS(i,0);
DFS(i,0,s);
int tmp=max(f[i][0],f[i][1]);
DFS(i,0,t);
tmp=max(tmp,max(f[i][0],f[i][1]));
ans+=tmp;
}
printf("%d\n",ans);
}
int main()
{
freopen("game.in","r",stdin);
freopen("game.out","w",stdout);
data_in();
work();
return 0;
}