题意: 网址.
方法: 强联通分量tarjan缩点.
解析: 读完题后画一下样例 (这样例并没有什么用),所以我们还是草拟一组样例来做吧,于是找到那个 Victoria的舞会 3 的样例来看
先不管这个图输出什么,首先来分析查询每个人时候的状态。
每一个人是杀手的概率是 1 / n(这是毋庸置疑的)
{1,2,3} 是一个强连通分量,如果我们从1开始查询,那么他是杀手的概率是1/n,但是只要知道了1,我们就可以 知道2,3是不是杀手,所以把1,2,3看在一起,知道1,就知道2,3,所以这一个强连通分量中有杀手的概率就是 1/n。于是喜闻乐见地进一步分析,发现这时 每一个强连通分量是杀手的概率是1/n,那么答案就应该是1-3/n 应 该是 4/7 (加法原理)
主旨: 缩点后重新建图,然后对于ans个强连通分量做查询;
注意: 如果在查询了n-1个点之后,还没有找到杀手,则第n个点一定是杀手,所以此时的概率就应该 增加 1/n ,即最后一 个点不用搜索。
#include <stdio.h>
#include <string.h>
#include <algorithm>
using namespace std ;
struct node
{
int to ;
int next ;
int from ;
};
node edge[300001] ;
node edge2[300001] ;
int head[100001] ;
int z[100001] ;
int inz[100001] ;
int low[100001] ;
int deep[100001] ;
int belong[100001] ;
int num[100001] ;
int in[100001] ;
int head2[100001] ;
int top , tot , cnt , ans , cnt2;
void init()
{
memset(head , -1 , sizeof(head)) ;
memset(head2 , -1 , sizeof(head2)) ;
cnt2 = 1 ;
cnt = 1 ;
}
void edgeadd(int from , int to)
{
edge[cnt].from = from ;
edge[cnt].to = to ;
edge[cnt].next = head[from] ;
head[from] = cnt ++ ;
}
void edgeadd2(int from , int to)
{
edge2[cnt2].from = from ;
edge2[cnt2].to = to ;
edge2[cnt2].next = head2[from] ;
head2[from] = cnt2 ++ ;
}
void tarjan(int x)
{
deep[x] = low[x] = ++tot ;
inz[x] = 1 , z[++top] = x ;
for(int i = head[x] ; i != -1 ; i = edge[i].next)
{
int to = edge[i].to ;
if(!deep[to])
{
tarjan(to) ;
low[x] = min(low[x] , low[to]) ;
}else if(inz[to]) low[x] = min(low[x] , deep[to]) ;
}
if(low[x] == deep[x])
{
ans ++ ;
int t ;
do
{
t = z[top--] ;
inz[t] = 0 ;
belong[t] = ans ;
num[ans] ++ ;
}while(t!=x) ;
}
}
int jud(int i)
{
for(int j = head2[i] ; j != -1 ; j = edge2[j].next)
{
int to = edge2[j].to ;
if(in[to] == 1) return 0 ;
}
return 1 ;
}
int main()
{
int n , m ;
init() ;
scanf("%d%d" , &n , &m) ;
for(int i = 1 ; i <= m ; i++)
{
int x , y ;
scanf("%d%d" , &x , &y) ;
edgeadd(x , y) ;
}
if(n == 1)
{
printf("1.000000\n") ;
return 0 ;
}
for(int i = 1 ; i <= n ; i++)
{
if(!deep[i])
{
tarjan(i) ;
}
}
for(int i = 1 ; i <= cnt ; i++)
{
int x = edge[i].from , y = edge[i].to ;
if(belong[x] != belong[y])
{
in[belong[y]] ++ ;
edgeadd2(belong[x] , belong[y]) ;
}
}
int tmp = 0 ;
for(int i = 1 ; i <= ans ; i++)
{
if(in[i] == 0) tmp ++ ;
}
int flag = 0 ;
for(int i = 1 ; i <= ans ; i++)
{
if(tmp > 1 && in[i]==0 && num[i] == 1)
{
if(jud(i))
{
tmp -- ;
break ;
}
}
}
printf("%.6lf\n" , (double)(n-tmp)/n) ;
}