1. 程式人生 > >[廣附10.7多校聯測]“殺人”遊戲

[廣附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;
}