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;
}