杀人游戏
Description
一位冷血的杀手潜入 Na-wiat,并假装成平民。警察希望能在 N 个人里面,查出谁是杀手。警察能够对每一个人进行查证,假如查证的对象是平民,他会告诉警察,他认识的人, 谁是杀手, 谁是平民。 假如查证的对象是杀手, 杀手将会把警察干掉。现在警察掌握了每一个人认识谁。每一个人都有可能是杀手,可看作他们是杀手的概率是相同的。问:根据最优的情况,保证警察自身安全并知道谁是杀手的概率最大是多少?
Input
第一行有两个整数 N,M。
接下来有 M 行,每行两个整数 x,y,表示 x 认识 y(y 不一定认识 x) 。
Output
仅包含一行一个实数,保留小数点后面 6 位,表示最大概率。
Samples
Input
5 4
1 2
1 3
1 4
1 5
Output
0.800000
Hint
警察只需要查证 1。假如1是杀手,警察就会被杀。假如 1不是杀手,他会告诉警察 2,3,4,5 谁是杀手。而 1 是杀手的概率是 0.2,所以能知道谁是杀手但没被杀的概率是0.8。
对于 100%的数据有 1≤N≤100000, 0≤M≤300000
首先一读题感觉是个并查集,找fa[i] == i的点进行处理就是最优的,但仔细一想感觉很不对劲,如果是用并查集来处理的话,概率就非常难处理
这个题应该是涉及到联通块的问题,对于一个环,比如1->2 2->3 3->1的情况只需要调查其中一个点就能知道其中所有点的情况,如果是还有另外一个点比如说是4 -> 1,对于这种情况,我们就可以之调查一个点来解决
对于一个强联通的环,有两个节点指向这个环中的某个节点(比如a->1,b->3,环依然是上面由1、2、3组成的环),我们可以清楚的知道,从两个点中任意选一个就能够知道环中的情况:
情况1:杀手在环内或者是所选的点,就可以直接知道杀手的情况
情况2:杀手不在环内或者是所选的节点,那么最后一个节点一定是杀手
所以说,最后一个节点可以不处理
在处理这个提的时候要从入度为0且出度不为0的点出发,这样能够保证更优,会减少调查的次数,最大化最大概率
某节点入度为0,但是其指向的节点的入度 > 1可以在最后进行处理
如果缩点后成为一个节点(之前存在环且缩点后这个点的入度为0),在处理其他节点之后,这个环里面的元素仍然没有内处理,就需要再次花费一次操作来解决联通块里面的信息
对于独立的点,我们可以放到最后进行处理或者是不进行处理
/*** keep hungry and calm PushyTao!***/
const int maxn = 3e5 + 7;
int n, m;
struct pre
{
int u;
int v;
} a[maxn];
struct node
{
int v;
int nex;
} e[maxn];
node e2[maxn];
int head[maxn],head2[maxn];
int cnt, tot;
stack <int> st;
int col[maxn];
bool vis[maxn],visit[maxn];
int dfn[maxn], low[maxn];
int siz[maxn];
int zero,times;
int Stack[maxn],top;
void init()
{
for(int i = 1; i <= n; i++)
{
head[i] = -1;
head2[i] = -1;
}
}
void add(int u, int v)
{
e[cnt].v = v;
e[cnt].nex = head[u];
head[u] = cnt ++;
}
int inDeg[maxn];
void AddEdge(int u, int v)
{
e2[cnt].v = v;
e2[cnt].nex = head2[u];
head2[u] = cnt++;
inDeg[v] ++;/// u -> v indegree v ++
}
void tarjan(int x)
{
dfn[x] = low[x] = ++ times;
Stack[++top] = x;
visit[x] = 1;
for(int i = head[x]; ~i; i = e[i].nex)
{
if(!dfn[e[i].v])
{
tarjan(e[i].v);
if(low[e[i].v] < low[x]) low[x] = low[e[i].v];
}
else if(visit[e[i].v] && dfn[e[i].v] < low[x])
{
low[x] = dfn[e[i].v];
}
}
if(low[x] == dfn[x])
{
tot++;
while(Stack[top + 1] != x){
visit[Stack[top]] = 0;
col[Stack[top]] = tot;
siz[tot] ++;
top --;
}
}
}
int main()
{
n = read, m = read;
init();/// initialize
for(int i = 1; i <= m; i++)
{
a[i].u = read;
a[i].v = read;
add(a[i].u, a[i].v);
}
// puts("ok");
for(itn i = 1; i <= n; i++)
{
if(dfn[i] == 0) tarjan(i);
}
// puts("ok");
cnt = 0;/// reset cnt as ZERO
for(int i = 1; i <= n; i++)
{
for(int j = head[i]; ~j; j = e[j].nex)
{
if(col[i] == col[e[j].v] || vis[col[e[j].v]]) continue;
AddEdge(col[i], col[e[j].v]);
vis[col[e[j].v]] = true;/// marked as 1
}
for(int j = head[i]; ~j; j = e[j].nex)
{
vis[col[e[j].v]] = false;/// marked as 0
}
}
// puts("ok");
// cout<<tot<<endl;
int falg = 0;/// tot == the number of nodes after tarjan
for (int i = 1; i <= tot; ++i)
{
if(inDeg[i] == 0) zero ++;/// the number of nodes indegee is zero
if(inDeg[i] || siz[i] >= 2) continue;
if(falg == 0)
{
int ju = 0;
for(int j = head2[i]; ~j; j = e2[j].nex)
{
if(inDeg[e2[j].v] <= 1)
{
ju = 1;
break;
}
}
if(ju == 0) zero --, falg = 1;
}
}
// cout << zero <<endl;
printf("%.6lf\n", (double)(n - zero) / (1.0 * n));
return 0;
}