【洛谷P4819】杀人游戏

题目

题目链接:https://www.luogu.com.cn/problem/P4819

一位冷血的杀手潜入 Na-wiat,并假装成平民。警察希望能在 \(n\) 个人里面,查出谁是杀手。警察能够对每一个人进行查证,假如查证的对象是平民,他会告诉警察,他认识的人,谁是杀手,谁是平民。假如查证的对象是杀手,杀手将会把警察干掉。现在警察掌握了每一个人认识谁。每一个人都有可能是杀手,可看作他们是杀手的概率是相同的。

问:根据最优的情况,保证警察自身安全并知道谁是杀手的概率最大是多少。

思路

考虑到在一个强连通分量里,问任意一个人情况,只要这个人不是杀手,那么就可以把这个强连通分量里所有人的身份得知。所以果断缩点。
那么就得到了一张 DAG。我们只需要将这张 DAG 中所有入度为 0 的点(也就是原先的一个强连通分量)询问即可得出所有人的身份。设入度为 0 的点有 \(k\) 个,那么只需要问 \(k\) 次即可。
但是考虑一种情况:已经得知其中 \(n-1\) 个人均为平民,那么排除法即可知道最后一个人是杀手。所以如果存在一个大小为 1 且没有入度的强连通分量,那么只需要问 \(k-1\) 次即可。
那么答案就是 \(1-\frac{k}{n}\)。特殊情况 \(k\) 要减一。
时间复杂度 \(O(n)\)

代码

#include <stack>
#include <cstdio>
#include <vector>
#include <cstring>
#include <algorithm>
using namespace std;

const int N=300010;
int head[N],dfn[N],low[N],deg[N],pos[N],U[N],V[N];
int n,m,tot,cnt,ans;
bool vis[N];
vector<int> scc[N];
stack<int> st;

struct edge
{
	int next,to;
}e[N];

void add(int from,int to)
{
	e[++tot].to=to;
	e[tot].next=head[from];
	head[from]=tot;
}

void tarjan(int x)
{
	dfn[x]=low[x]=++tot;
	st.push(x);
	vis[x]=1;
	for (int i=head[x];~i;i=e[i].next)
	{
		int v=e[i].to;
		if (!dfn[v])
		{
			tarjan(v);
			low[x]=min(low[x],low[v]);
		}
		else if (vis[v])
			low[x]=min(low[x],low[v]);
	}
	if (low[x]==dfn[x])
	{
		int y;
		cnt++;
		do {
			y=st.top(); st.pop();
			vis[y]=0; pos[y]=cnt;
			scc[cnt].push_back(y);
		} while (y!=x);
	}
}

int check()
{
	for (int i=1;i<=n;i++)
		if (!deg[i])
		{
			bool flag=1;
			for (int j=head[i];~j;j=e[j].next)
				if (deg[e[j].to]==1)
				{
					flag=0;
					break;
				}
			if (flag) return 1;
		}
	return 0;
}

int main()
{
	memset(head,-1,sizeof(head));
	scanf("%d%d",&n,&m);
	for (int i=1;i<=m;i++)
	{
		scanf("%d%d",&U[i],&V[i]);
		add(U[i],V[i]);
	}
	tot=0;
	for (int i=1;i<=n;i++)
		if (!dfn[i]) tarjan(i);
	for (int i=1;i<=m;i++)
		if (pos[U[i]]!=pos[V[i]]) deg[pos[V[i]]]++;
	for (int i=1;i<=cnt;i++)
		if (!deg[i]) ans++;
	memset(deg,0,sizeof(deg));
	for (int i=1;i<=m;i++)
		deg[V[i]]++;
	printf("%0.6lf",1.0-1.0*(ans-check())/n);
	return 0;
}
posted @ 2020-06-11 19:46  stoorz  阅读(176)  评论(0编辑  收藏  举报