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

浙公网安备 33010602011771号