题目描述
一位冷血的杀手潜入Na-wiat,并假装成平民。警察希望能在
N
N
个人里面,查出谁是杀手。警察能够对每一个人进行查证,假如查证的对象是平民,他会告诉警察,他认识的人,谁是杀手,谁是平民。假如查证的对象是杀手,杀手将会把警察干掉。现在警察掌握了每一个人认识谁。每一个人都有可能是杀手,可看作他们是杀手的概率是相同的。
问:根据最优的情况,保证警察自身安全并知道谁是杀手的概率最大是多少?
输入输出格式
输入格式:
第一行有两个整数 。 接下来有
M
M
行,每行两个整数 ,表示
x
x
认识 (
y
y
不一定认识 ,例如President同志) 。
注:原文zz敏感内容已替换
输出格式:
仅包含一行一个实数,保留小数点后面
6
6
位,表示最大概率。
输入输出样例
输入样例#1:
5 4
1 2
1 3
1 4
1 5
输出样例#1:
0.800000
说明
警察只需要查证 。假如 1 1 是杀手,警察就会被杀。假如 不是杀手,他会告诉警察 2,3,4,5 2 , 3 , 4 , 5 谁是杀手。而 1 1 是杀手的概率是,所以能知道谁是杀手但没被杀的概率是 0.8 0.8 。
对于 100% 100 % 的数据有 1≤N≤100000,0≤M≤300000 1 ≤ N ≤ 100000 , 0 ≤ M ≤ 300000
分析:
假如我们调查了一个人,有
1n
1
n
概率被杀,有
kn
k
n
概率调查到凶手,
k
k
表示与当前人认识的人数。剩下的就是知道这个人以及他认识的人都不是凶手的概率,而下次调查人数变少,概率变大,但是要乘上当前触发这个条件的概率,所以其实都是。
显然我们调查一次没有找到凶手,下次调查一定调查那些已经被知道不是凶手的人,也就是说,我们调查一个人,可以知道他间接认识的人的身份。
因为原图是有向图,我们可以先缩点,显然一个联通分量只需要调查一个人。那么答案是入度为
0
0
的点的个数。
但是这有例外,比如说,如果全部人个不认识,我们只需要调查
n−1
n
−
1
个人即可,并不是所有人,可以理解为有一个入度为
0
0
的点的情况在调查了其他的点后,不需要调查就知道了。
也就是说,如果缩点后的图存在一个点,他的入度为,而且这个联通分量缩点前也是一个点而不是一个环,并且与他认识的点入度都大于 1 1 (也就是可以通过其他人调查,不调查这个点不影响),那么总调查数就可以减,但是只能允许不调查一个这样的点。答案就是 1−numn 1 − n u m n , num n u m 为总调查数。
代码:
#include <iostream>
#include <cstdio>
#include <cmath>
#include <vector>
const int maxn=1e5+7;
const int maxe=3e5+7;
using namespace std;
int flag,n,m,num,x,y,cnt,bcnt;
int r[maxn],dfn[maxn],low[maxn],belong[maxn],d[maxn],ls[maxn],vis[maxn];
vector <int> sta;
struct edge{
int x,y,next;
}g[maxe];
void dfs(int x)
{
dfn[x]=low[x]=++cnt;
vis[x]=1; sta.push_back(x);
for (int i=ls[x];i>0;i=g[i].next)
{
int y=g[i].y;
if (!dfn[y])
{
dfs(y);
low[x]=min(low[x],low[y]);
}
else
{
if (vis[y]) low[x]=min(low[x],dfn[y]);
}
}
if (low[x]==dfn[x])
{
bcnt++;
int y=0;
while (y!=x)
{
y=sta.back();
sta.pop_back();
belong[y]=bcnt;
vis[y]=0;
}
}
}
int main()
{
scanf("%d%d",&n,&m);
for (int i=1;i<=m;i++)
{
scanf("%d%d",&x,&y);
g[i].x=x;
g[i].y=y;
g[i].next=ls[x];
ls[x]=i;
r[y]++;
}
for (int i=1;i<=n;i++)
{
if (!dfn[i]) dfs(i);
}
num=0;
for (int i=1;i<=m;i++)
{
x=g[i].x,y=g[i].y;
if (belong[x]!=belong[y]) d[belong[y]]++;
}
for (int i=1;i<=bcnt;i++)
{
if (!d[i]) num++;
}
for (int i=1;i<=n;i++)
{
if ((!d[belong[i]]) && (!r[i]))
{
int rec=0;
for (int j=ls[i];j>0;j=g[j].next)
{
int y=g[j].y;
if (r[y]<=1)
{
rec=1;
break;
}
}
if (!rec)
{
flag=1;
break;
}
}
}
double ans=1-(double)(num-flag)/(double)n;
printf("%.6lf",ans);
}