[廣附10.7多校聯測]“殺人”遊戲
目錄
題目描述
有很多資訊學選手在外出比賽時,在宿舍裡都會玩一種“殺人遊戲”。本題的規則比正式的遊戲簡單很多。假設現在的遊戲剩下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;
}