BZOJ2438:[中山市选2011]杀人游戏(强连通分量)

Description

一位冷血的杀手潜入 Na-wiat,并假装成平民。警察希望能在 N 个人里面,查出谁是杀手。警察能够对每一个人
进行查证,假如查证的对象是平民,他会告诉警察,他认识的人, 谁是杀手, 谁是平民。 假如查证的对象是杀
手, 杀手将会把警察干掉。现在警察掌握了每一个人认识谁。每一个人都有可能是杀手,可看作他们是杀手的概
率是相同的。问:根据最优的情况,保证警察自身安全并知道谁是杀手的概率最大是多少?

Input

第一行有两个整数 N,M。
接下来有 M 行,每行两个整数 x,y,表示 x 认识 y(y 不一定认识 x,例如[政治敏感]同志) 。

Output

仅包含一行一个实数,保留小数点后面 6 位,表示最大概率。

Sample Input

5 4
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

Solution 

显然要先缩一下点……然后发现只有缩点后的图上入度为0的点需要查验。

统计一波入度为0的点然后直接计算就可以拿到28分的好成绩

发现一种奇怪的情况……比如1->2,3->2,。先查一下1,然后顺带可以知道2的身份。如果这个时候警察还没死,发现剩下了一个3,那么这个3就被钦定是杀手了= =

也就是说,如果一个点入度为0且不是由环缩成的的点,若它连向的点入度都不为1(也就是说它连向的点的身份都可以从别的点得知),那么这个点就允许不被调查,不过显然不被调查的点只能有一个。

Code

 1 #include<iostream>
 2 #include<cstring>
 3 #include<cstdio>
 4 #define N (300009)
 5 using namespace std;
 6 
 7 struct Edge{int to,next;}edge[N];
 8 int n,m,cnt,emm,flag,u[N],v[N],Ind[N];
 9 int Dfn[N],Low[N],stack[N],ID[N],Num[N];
10 int top,id_num,dfs_num;
11 int head[N],num_edge;
12 bool vis[N];
13 
14 void add(int u,int v)
15 {
16     edge[++num_edge].to=v;
17     edge[num_edge].next=head[u];
18     head[u]=num_edge;
19 }
20 
21 void Tarjan(int x)
22 {
23     Dfn[x]=Low[x]=++dfs_num;
24     stack[++top]=x; vis[x]=true;
25     for (int i=head[x]; i; i=edge[i].next)
26         if (!Dfn[edge[i].to])
27             Tarjan(edge[i].to),Low[x]=min(Low[x],Low[edge[i].to]); 
28         else if (vis[edge[i].to])
29             Low[x]=min(Low[x],Dfn[edge[i].to]);
30     if (Low[x]==Dfn[x])
31     {
32         vis[x]=false; ID[x]=++id_num; Num[id_num]++;
33         while (stack[top]!=x)
34         {
35             vis[stack[top]]=false;
36             Num[id_num]++;
37             ID[stack[top--]]=id_num;
38         }
39         top--;
40     }
41 }
42 
43 int main()
44 {
45     scanf("%d%d",&n,&m);
46     for (int i=1; i<=m; ++i)
47         scanf("%d%d",&u[i],&v[i]),add(u[i],v[i]);
48     for (int i=1; i<=n; ++i)
49         if (!Dfn[i]) Tarjan(i);
50         
51     memset(head,0,sizeof(head)); num_edge=0;
52     for (int i=1; i<=m; ++i)
53         if (ID[u[i]]!=ID[v[i]])
54             add(ID[u[i]],ID[v[i]]),Ind[ID[v[i]]]++;
55 
56     for (int i=1; i<=id_num; ++i) if (!Ind[i])
57     {
58         cnt++;
59         if (Num[i]==1) flag=true;
60         for (int j=head[i]; j; j=edge[j].next)
61             if (Ind[edge[j].to]==1) flag=false;
62         if (flag) emm=1;
63     }
64     printf("%.6lf\n",1.0-1.0*(cnt-emm)/n);
65 }
posted @ 2018-09-09 16:13  Refun  阅读(175)  评论(0编辑  收藏  举报