【BZOJ2438】【中山市选2011】杀人游戏
【问题描述】
一位冷血的杀手潜入 Na-wiat,并假装成平民。警察希望能在 N 个人里面,
查出谁是杀手。
警察能够对每一个人进行查证,假如查证的对象是平民,他会告诉警察,他
认识的人, 谁是杀手, 谁是平民。 假如查证的对象是杀手, 杀手将会把警察干掉。
现在警察掌握了每一个人认识谁。
每一个人都有可能是杀手,可看作他们是杀手的概率是相同的。
问:根据最优的情况,保证警察自身安全并知道谁是杀手的概率最大是多
少?
对于 100%的数据有 1≤N ≤ 10 0000,0≤M ≤ 30 0000
【分析】
很简单的一道题。题目给了一个有向图,每查未知的一个人,猜错的概率增加1/n。如果出现环的话,那么我们只要在环上任意找一点,只要这人不是杀手,就可以直接推出整个环,也就是说一个环上猜错的概率只有1/n。然后我们就可以先强连通缩点。
缩点以后,一个人是不是杀手可以由认识他的人(前继)推过来,因此我们可以先选入度为0的点,也只有这些点会有1/n的概率猜错。这样直接算出缩点后入度为0的点就行了。
值得注意的是,有一个需要特判的情况,如下数据:
3 1
1 2
这时入度为0的点有两个,也就是答案是1⁄3,但实际上我们只要判断玩1,2就能直接知道3是不是杀手,所以3不可能猜错,正确答案是2/3。最后判断一下是否存在大小为1的强连通分量然后-1就行了。
【代码】
/************************************************************** Problem: 2438 User: N_C_Derek Language: C++ Result: Accepted Time:716 ms Memory:19260 kb ****************************************************************/ #include <iostream> #include <cstdio> #include <cstring> #include <algorithm> using namespace std; struct node { int v,next; }e[500000]; int n,m,et,ct,st,Index; int d[200000],first[200000],s[200000],back_up[600000][3],color[200000],dfn[200000],low[200000],num[200000]; bool p[200000],instack[200000]; void insert(int x,int y) { e[++et].v = y; e[et].next = first[x]; first[x] = et; } void dfs(int x) { dfn[x] = low[x] = ++Index; instack[x] = true; s[++st] = x; for (int i = first[x];i ;i = e[i].next) { if (!dfn[e[i].v]) { dfs(e[i].v); low[x] = min(low[x],low[e[i].v]); } else if (instack[e[i].v]) low[x] = min(low[x],dfn[e[i].v]); } if (dfn[x] == low[x]) { ct ++; do { color[s[st]] = ct; instack[s[st]] = false; num[ct] ++; st --; } while (s[st + 1] != x); } } int main() { scanf("%d %d",&n,&m); memset(dfn,0,sizeof(dfn)); for (int i = 1;i <= m;i ++) { int x,y; scanf("%d %d",&x,&y); back_up[i][1] = x,back_up[i][2] = y; insert(x,y); } for (int i = 1;i <= n;i ++) if (!dfn[i]) dfs(i); for (int i = 1;i <= m;i ++) if (color[back_up[i][1]] != color[back_up[i][2]]) d[color[back_up[i][2]]] ++; int ans = 0; for (int i = 1;i <= ct;i ++) if (d[i] == 0) ans++; for (int i = 1;i <= ct;i ++) if (ans > 1 && d[i] == 0 && num[i] == 1) {ans --;break;} printf("%.6lf",1 - (double)ans / n); }