BZOJ 2438: [中山市选2011]杀人游戏 Tarjan
2438: [中山市选2011]杀人游戏
Description
一位冷血的杀手潜入 Na-wiat,并假装成平民。警察希望能在 N 个人里面,查出谁是杀手。警察能够对每一个人
进行查证,假如查证的对象是平民,他会告诉警察,他认识的人, 谁是杀手, 谁是平民。 假如查证的对象是杀
手, 杀手将会把警察干掉。现在警察掌握了每一个人认识谁。每一个人都有可能是杀手,可看作他们是杀手的概
率是相同的。问:根据最优的情况,保证警察自身安全并知道谁是杀手的概率最大是多少?
Input
第一行有两个整数 N,M。
接下来有 M 行,每行两个整数 x,y,表示 x 认识 y(y 不一定认识 x,例如HJT同志) 。
Output
仅包含一行一个实数,保留小数点后面 6 位,表示最大概率。
Sample Input
5 4
1 2
1 3
1 4
1 5
1 2
1 3
1 4
1 5
Sample Output
0.800000
HINT
警察只需要查证 1。假如1是杀手,警察就会被杀。假如 1不是杀手,他会告诉警
察 2,3,4,5 谁是杀手。而 1 是杀手的概率是 0.2,所以能知道谁是杀手但没被杀的概
率是0.8。对于 100%的数据有 1≤N ≤ 10 0000,0≤M ≤ 30 0000
思路:
本题的概率没什么卵用, 我们发现要求的就是至少要问多少个人才能看到整张图。缩点, 变成一棵树 对所有入度为0的进行询问, 但是有这样一种情况就是有一个点入度为0, 但是可以问到除了他以外的所有人, 这样他身份确定就不用问他了,情况需要特判 。
Code :
#include <cstdio> #include <cstring> #include <algorithm> #include <iostream> #include <stack> #include <cctype> #define ms(a) (memset(a, 0, sizeof(a))) #define min(a, b) (a<b?a:b) using namespace std; const int N = 111000, M = 310100; int head[N], to[M], nxt[M], cnt; int dfn[N], low[N], place[N], tot; int ins[N], idx; int ind[N]; stack<int>s; int x[M], y[M]; inline char nc() { static char buf[100000], *p1, *p2; return p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 100000 ,stdin), p1==p2)?EOF:*p1++; } inline char gc() { char c = nc(); while(isspace(c)) c = nc(); return c; } inline int read() { int x = 0; char c = nc(); while(!isdigit(c))c=nc(); while(isdigit(c)) {x=(x<<3)+(x<<1)+(c^48), c=nc();} return x; } int n=read(), m=read(); void add(int x, int y) { to[++cnt] = y; nxt[cnt] = head[x]; head[x] = cnt; } void Tarjan(int p) { dfn[p] = low[p] = ++idx; s.push(p); ins[p] = 1; for(int i=head[p];i;i=nxt[i]) { if(!dfn[to[i]]) { Tarjan(to[i]); low[p] = min(low[p], low[to[i]]); } else if(ins[to[i]]) { low[p] = min(low[p], dfn[to[i]]); } } if(low[p]==dfn[p]) { tot++; int t = 0; while(t!=p) { t = s.top();s.pop(); ins[t] = 0; place[t] = tot; } } } int rebuild() { int ret = 0; memset(head, 0, sizeof(head)); cnt = 0; for(int i=1;i<=m;i++) { if(place[x[i]]!=place[y[i]]) { add(place[x[i]], place[y[i]]); ind[place[y[i]]] ++; } } for(int i=1;i<=tot;i++) { if(!ind[i]) ret++; } return ret; } bool check(int p) { bool flag = 1; for(int i=head[p];i;i=nxt[i]) { if(ind[to[i]]<=1) flag = 0; } return flag ; } bool hav[N]; int main() { if(n==1) { puts("1.000000"); return 0; } for(int i=1;i<=m;i++) { x[i] = read(), y[i] = read(); add(x[i], y[i]); } int ans = 0, flg = 0; for(int i=1;i<=n;i++) if(!dfn[i]) Tarjan(i); ans = rebuild(); for(int i=1;i<=tot;i++) { if(ind[i]==0&&check(i)) flg = 1; } if(tot==1) ans = 1, flg = 0; printf("%.6lf\n", 1-((ans-flg)*(1.0/n))); }