[广附10.7多校联测]“杀人”游戏

目录

题目描述

输入

输出

输入样例

输出样例

样例解释

数据范围

题解


题目描述

有很多信息学选手在外出比赛时,在宿舍里都会玩一种“杀人游戏”。本题的规则比正式的游戏简单很多。假设现在的游戏剩下N个人,这里的人分为两类:恶魔、平民。
恶魔是知道其他哪些人是恶魔哪些人是平民的,而平民则不知道其他哪些人士恶魔哪些人士平民。现在这N个人,每个人都要指控另一个人。一个恶魔一定是指控一个平民,而一个平民指控的人可能是恶魔也可能是平民。现在给出这N个人指控的关系,问满足这种指控关系的前提下,最多可以有多少个恶魔?

输入

第一行,一个整数N。有N个人,编号是从1至N。
接下来有N行,每行一个整数,第i行的整数ai,表示的意义是:第i个人指控第ai个人。没有人会指控自己。

输出

一个整数,表示最多可以有多少只恶魔

输入样例

样例3
2
1
1

样例2 



1

样例3 







4

输出样例

样例1
2

样例2 
1

样例3
4

样例解释

第一个样例解释:杀手可能为2和3; 
第二个样例解释:杀手可能是任何1个人,但不可能多于1个,否则杀手就会指认自己人了。

数据范围

40%的数据n<25
80%的数据n<=2000 
100%的数据 2\leq N \leq 500000

题解

分析一下,可以发现这个图不具有连通性。 
可以发现图上的边的方向并没有卵用,因为坏人是不可以相互指认的,所以问题变成了选择一个点的集合,使得从这个集合出发的指认不指向这个集合中的任意元素,求集合的元素最多有多少个,如果这是一颗树的话就可以直接做最大独立集了。但是这不一定是一棵树。不过由于每个点只发出一条边可以证明这是一颗基环外向树。然后问题就简单了,找到环上的一条边,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;
}

猜你喜欢

转载自blog.csdn.net/qq_40155097/article/details/83144907