洛谷 P4819 【杀人游戏】


思路:

  1. 先不管概率,算一下最少需要查多少人才能知道谁是杀手。
  2. 若查A就可以知道B,那么连一条A ---> B的边,观察图可以轻松发现每个强连通分量中,只要查一个人就可以知道里面所有人的信息,因此先缩点
  3. 现在缩完点后的图是一个DAG,再次观察可以发现如果图中点X可以到达点Y,那么表示查X就可以知道Y的身份,那么只需查不能查别人的信息来获得他的信息的点,图中表示入度为0的点
  4. 但要注意一点,就是如果除了一个人剩下人都查了,那么通过排除法就能判断那个人就是杀手,所以缩点时要记录一下点内有多少个人,最后在算答案时,如果有一个点入度为0且里面只有一个人,答案 - 1(就减一次),但注意当图中只有一个点需要查的时候这种情况就不考虑了。但还有一种情况,就是如果这个入度为0且里面只有一个人的点所连出去的点入度为1,即没有其他点能到达它,那么这个点是必须选的
  5. 回来看概率,其实警察查到杀手的情况只会在我们必须查的人里出现,及入度为0的点,那么查到杀手的概率即为 最少查的人 / 所有人 ,那么安全的概率就是 (所有人 - 最少查的人) / 所有人

代码:

 1 #include <bits/stdc++.h>
 2 #define INF 0x3f3f3f3f
 3 using namespace std;
 4 int n, m, head[100010], num, dfn[100010], low[100010], sum[100010], z, col[100010], color, in[100010], ans;
 5 stack < int > pru;
 6 struct node
 7 {
 8     int next, to;
 9 }stu[300010];
10 void add(int x, int y)
11 {
12     stu[++num].next = head[x];
13     stu[num].to = y;
14     head[x] = num;
15     return;
16 }
17 void tarjan(int u)//缩点 
18 {
19     dfn[u] = low[u] = ++z;
20     pru.push(u);
21     for(int i = head[u]; i; i = stu[i].next)
22     {
23         int k = stu[i].to;
24         if(!dfn[k]) tarjan(k), low[u] = min(low[u], low[k]);
25         else if(!col[k]) low[u] = min(low[u], dfn[k]);
26     }
27     if(dfn[u] == low[u])
28     {
29         col[u] = ++color;
30         ++sum[color];
31         while(pru.top() != u)
32         {
33             col[pru.top()] = color;
34             ++sum[color];
35             pru.pop();
36         }
37         pru.pop();
38     }
39     return;
40 }
41 void rebuild()//重建边 
42 {
43     queue < pair < int, int > > G;
44     for(int u = 1; u <= n; ++u)
45     {
46         for(int i = head[u]; i; i = stu[i].next)
47         {
48             int k = stu[i].to;
49             if(col[k] != col[u]) ++in[col[k]], G.push(make_pair(col[u], col[k]));
50         }
51     }
52     memset(head, 0, sizeof(head));
53     num = 0;
54     while(!G.empty())
55     {
56         add(G.front().first, G.front().second);
57         G.pop();
58     }
59     return;
60 }
61 int main()
62 {
63     scanf("%d %d", &n, &m);
64     for(int i = 1, x, y; i <= m; ++i)
65     {
66         scanf("%d %d", &x, &y);
67         add(x, y);
68     }
69     for(int i = 1; i <= n; ++i) if(!dfn[i]) tarjan(i);
70     rebuild();
71     int decrease = 0;
72     for(int u = 1; u <= color; ++u)//看入度 
73     {
74         if(in[u] == 0)
75         {
76             ++ans;
77             if(sum[u] == 1)
78             {
79                 int de = 1;
80                 for(int i = head[u]; i; i = stu[i].next)//判断是否有其他点能到达 
81                 {
82                     int k = stu[i].to;
83                     if(in[k] == 1) de = 0;
84                 }
85                 if(de)
86                 {
87                     decrease = 1;
88                 }
89             }
90         }
91     }
92     if(decrease && ans > 1) --ans;//判断是否存在入度为0但只有一个人且连出去的点有其他点也能到达 
93     if(n == 1) ans = 0;//特判 
94     printf("%.6lf", 1.0 * (n - ans) / n);
95     return 0;
96 }

 

posted @ 2020-07-22 16:34  louis_11  阅读(199)  评论(0)    收藏  举报