这是一道好题啊。
首先想这样一个问题,如果存在一个环,我们只需要问其中一个人,若它不是杀手,那么我们便知道了环内所有人的身份(根据最优的情况),因此可以先tarjan缩一下环。
然后,对于每一个入度为0的强连通分量,我们都是要去询问的。所以ans=入度为0的强连通分量的个数。
存在一种特殊情况:入度为零,大小为1的强连通分量,且这个单点的所有出边指向的点所在的强连通分量 入度都大于等于2,及它指向的所有点都能被其他点访问到。这时我们便无需访问这个点,因为通过访问其他点我们就可以知道其他n-1个人的身份,而剩下一个排除就好了。
#include<iostream>
#include<cstdio>
#include<cstring>
#define N 100005
using namespace std;
int n,m,scc,cnt,cnt0,ans,top,tot;
int head[N],head0[N],ind[N];
int low[N],dfn[N],stack[N],belong[N],num[N];
bool inset[N],v[N];
int next[300005],next0[300005],list[300005],list0[300005];
inline int read()
{
int a=0,f=1; char c=getchar();
while (c<'0'||c>'9') {if (c=='-') f=-1; c=getchar();}
while (c>='0'&&c<='9') {a=a*10+c-'0'; c=getchar();}
return a*f;
}
inline void insert(int x,int y)
{
next[++cnt]=head[x];
head[x]=cnt;
list[cnt]=y;
}
inline void insert0(int x,int y)
{
next0[++cnt0]=head0[x];
head0[x]=cnt0;
list0[cnt0]=y;
ind[y]++;
}
void dfs(int x)
{
dfn[x]=low[x]=++tot;
inset[x]=1;
stack[++top]=x;
for (int i=head[x];i;i=next[i])
if (!dfn[list[i]])
{
dfs(list[i]);
low[x]=min(low[x],low[list[i]]);
}
else if (inset[list[i]]) low[x]=min(low[x],dfn[list[i]]);
if (dfn[x]==low[x])
{
int i=-1; scc++;
while (i!=x)
{
i=stack[top--];
belong[i]=scc;
inset[i]=0;
num[scc]++;
}
}
}
inline void tarjan()
{
for (int i=1;i<=n;i++) if (!dfn[i]) dfs(i);
}
inline void rebuild()
{
for (int x=1;x<=n;x++)
{
for (int i=head[x];i;i=next[i])
if (belong[x]!=belong[list[i]]&&!v[belong[list[i]]])
v[belong[list[i]]]=1,insert0(belong[x],belong[list[i]]);
for (int i=head[x];i;i=next[i])
if (belong[x]!=belong[list[i]]) v[belong[list[i]]]=0;
}
}
inline bool judge(int x)
{
if (ind[x]!=0||num[x]!=1) return 0;
for (int i=head0[x];i;i=next0[i])
if (ind[list0[i]]==1) return 0;
return 1;
}
int main()
{
n=read(); m=read();
for (int i=1;i<=m;i++)
{
int u=read(),v=read();
insert(u,v);
}
tarjan();
rebuild();
for (int i=1;i<=scc;i++)
if (!ind[i]) ans++;
for (int i=1;i<=scc;i++)
if (judge(i)) {ans--; break;}
printf("%.6lf",(double)(n-ans)/n);
return 0;
}