BZOJ 2438: [中山市选2011]杀人游戏

Description

一位冷血的杀手潜入 Na-wiat,并假装成平民。警察希望能在 N 个人里面,查出谁是杀手。警察能够对每一个人
进行查证,假如查证的对象是平民,他会告诉警察,他认识的人, 谁是杀手, 谁是平民。 假如查证的对象是杀
手, 杀手将会把警察干掉。现在警察掌握了每一个人认识谁。每一个人都有可能是杀手,可看作他们是杀手的概
率是相同的。问:根据最优的情况,保证警察自身安全并知道谁是杀手的概率最大是多少?

Input

第一行有两个整数 N,M。
接下来有 M 行,每行两个整数 x,y,表示 x 认识 y(y 不一定认识 x,例如|HJT|同志) 。

Output

仅包含一行一个实数,保留小数点后面 6 位,表示最大概率。

Sample Input

5 4

1 2

1 3

1 4

1 5

Sample Output

0.800000

HINT

警察只需要查证 1。假如1是杀手,警察就会被杀。假如 1不是杀手,他会告诉警

察 2,3,4,5 谁是杀手。而 1 是杀手的概率是 0.2,所以能知道谁是杀手但没被杀的概

率是0.8。对于 100%的数据有 1≤N ≤ 10 0000,0≤M ≤ 30 0000

数据已加强!

考虑如果这个图是一颗树 , 那么肯定选树根是最优的(有向的树)。
由于一个强连通分量里的点只要知道一个就可以知道所有的点 , 而且选每个点的代价都是 1/n
所以可以Tarjan缩点 , 将图变成一个DAG那么最优方案要算的点就是DAG上每个有根树的根节点。
也就是入度为0的点的个数。
但是有可能存在这种情况:知道了n-1个点的身份,还剩一个。
那么剩的这个点的所有儿子一定是都可以根据其他的点(也就是除了根以外的点)来知道身份。
显然,这样的点最多只能有一个。

#include<algorithm>
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<queue>
#include<vector>
#include<set>
#include<map>
using namespace std;
const int N = 1e5+10;
inline int read()
{
    register int x = 0 , f = 0; register char c = getchar();
    while(c < '0' || c > '9') f |= c == '-' , c = getchar();
    while(c >= '0' && c <= '9') x = (x << 3) + (x << 1) + c - '0' , c = getchar();
    return f ? -x : x;
}
int n , m , cnt , top , tot , num;
int head[N] , vis[N] , sta[N] , dfn[N] , low[N] , col[N] , du[N];
vector<int> V[N];
struct edge{ int u , v , nex; } e[N*3];
inline void add(int u , int v) { e[++cnt].u = u; e[cnt].v = v; e[cnt].nex = head[u]; head[u] = cnt; return ; }

void Tarjan(int x)
{
	low[x] = dfn[x] = ++tot; vis[x] = 1; sta[++top] = x;
	for(int i = head[x] , v; i ; i = e[i].nex)
	{
		v = e[i].v;
		if(!dfn[v]) Tarjan(v) , low[x] = min(low[x] , low[v]);
		else if(vis[v]) low[x] = min(low[x] , dfn[v]);
	}
	if(low[x] == dfn[x])
	{
		int t; num++;
		do t = sta[top--] , vis[t] = 0 , col[t] = num; while(t != x);
	}
	return ;
}

int main()
{
	n = read(); m = read();
	if(n == 1) { puts("1.000000"); return 0; }
	for(int i = 1 , u , v ; i <= m ; ++i) u = read() , v = read() , add(u , v);
	for(int i = 1 ; i <= n ; ++i) if(!dfn[i]) Tarjan(i);
	if(num == 1) { printf("%.6f\n" , 1.0 - (1.0 / n)); return 0; }
	
	for(int i = 1 ; i <= m ; ++i)
		if(col[e[i].u] != col[e[i].v])
			V[col[e[i].u]].push_back(col[e[i].v]) , du[col[e[i].v]]++;
	int ans = 0;
	for(int i = 1 ; i <= num ; ++i) if(du[i] == 0) ans++;
	int flag1 = 0 , flag2 = 0;
	for(int i = 1 ; i <= num ; ++i) if(du[i] == 0)
	{
		flag2 = 1;
		for(int j = 0 ; j < V[i].size() ; ++j)
			if(du[V[i][j]] <= 1) { flag2 = 0; break; }
		if(flag2) { flag1 = 1; break; }
	}
	printf("%.6f\n" , 1.0 - (1.0 * (ans - flag1) * (1.0 / n)));
	return 0;
}
posted @ 2020-02-19 16:51  沙野博士  阅读(152)  评论(0)    收藏  举报