Time:2016.05.21
Author:xiaoyimi
转载注明出处谢谢
传送门
思路:
首先把我难住的是怎么计算概率
一开始好像进入了思维误区,感觉计算这玩意非常困难,想了好久都没有想出来(题意理解不够透彻)
后来想明白,实际上警察就是两种结果——查到犯人或死亡,而死亡事件一定是包含在“调查未知身份的人”,也就是说调查未知身份的人越多,死亡概率就越高,所以我们要求的问题就是警察要尽可能少调查未知身份的人
给出公式
P死亡=调查的未知身份的人数总人数
“死亡”和”存活”是对立事件,而”存活”等价于”找到犯人”
所以
ans=1−P死亡
问题转化为求调查最少的未知身份人的人数
同时我们发现,对于一个环(1认识2,2认识3,3认识1),我们是可以把他们看作一个人的,也就是说,只要调查了他们其中一个,其他两个我们一定可以安全到达(知道他是犯人或平民)
对于这个问题,我们使用tarjan对图进行缩点处理,然后我们所要调查的对象就是重构图中那些入度为0的点(没有人知道这些点里的人的身份),调查的未知身份的人数就是这些点的数量
(如果其中入度不为0,那么我们一定可以从一个入度为0的点走到这里,即我们调查这个点时是知道这里面的人的身份的)
注意:
有一个特殊情况,如果说至少有一个点,它只有一个人,他是完全孤立的(即没有人知道这个人的身份,他对外面的世界也一无所知)或者他认识的每个人都被除他以外的其他人所认识(即它所能到达的点的入度>1),那么我们在安全调查完除他以外的其他人后,他一定是杀手(但在图上他属于未知身份的人),我们不必去调查他,直接抓起来就可以了,所以调查未知身份的人数要–
(我们可以想得简单些,比如n=1时这个人一定是杀手,即
P存活=1
)
代码:
#include<bits/stdc++.h>
#define M 100003
using namespace std;
int in()
{
int t=0;char ch=getchar();
while (!isdigit(ch)) ch=getchar();
while (isdigit(ch)) t=(t<<1)+(t<<3)+ch-48,ch=getchar();
return t;
}
stack<int>S;
int n=in(),m=in(),tot,cnt;
int dfn[M],low[M],first[M],First[M],belong[M],jin[M],siz[M];
bool vis[M];
struct edge{int v,next;}e[M*3],E[M*3];
void add(int y,int x){e[++tot]=(edge){y,first[x]};first[x]=tot;}
void Add(int x,int y){E[++tot]=(edge){y,First[x]};First[x]=tot;}
void dfs(int x)
{
low[x]=dfn[x]=++cnt;
vis[x]=1;
S.push(x);
for (int i=first[x];i;i=e[i].next)
if (!dfn[e[i].v])
dfs(e[i].v),
low[x]=min(low[e[i].v],low[x]);
else if (vis[e[i].v])
low[x]=min(low[x],dfn[e[i].v]);
if (dfn[x]==low[x])
{
siz[x]=0;
for (int y=-1;y!=x;y=S.top(),S.pop())
belong[S.top()]=x,
siz[x]++,
vis[S.top()]=0;
}
}
main()
{
for (int i=1;i<=m;i++) add(in(),in());
tot=0;
for (int i=1;i<=n;i++)
if (!dfn[i]) dfs(i);
for (int i=1;i<=n;i++)
for (int j=first[i];j;j=e[j].next)
if (belong[i]!=belong[e[j].v])
Add(belong[i],belong[e[j].v]),
jin[belong[e[j].v]]++;
int ans=0;
for (int i=1;i<=n;i++)
if (!vis[belong[i]]&&!jin[belong[i]])
vis[belong[i]]=1,
ans++;
for (int i=1;i<=n;i++)
if (siz[belong[i]]==1&&vis[belong[i]])
{
bool flag=0;
for (int j=First[belong[i]];j;j=E[j].next)
if (jin[E[j].v]<=1) {flag=1;break;}
if (!flag) {ans--;break;}
}
printf("%.6lf",1.0-1.0*ans/n);
}