[BZOJ 2438][中山市选2011]杀人游戏

2438: [中山市选2011]杀人游戏

Time Limit: 10 Sec  Memory Limit: 128 MB
Submit: 2524  Solved: 723
[Submit][Status][Discuss]

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


数据已加强!

dg的杂题选讲...熟悉的Tarjan OwO(然而打了4遍才过QAQ)

题解

当我们看到一大坨有向关系的时候似乎就该考虑 $Tarjan$ 了哈?

我们可以发现对于每个 $SCC$ 都只需要问一个人就可以得知整个 $SCC$ 以及这个 $SCC$ 连接到的其他 $SCC$ 的信息, 那么我们只要问那些入度为 $0$ 的 $SCC$ 中的一个人就行了OwO

由于每个人是凶手的概率都是 $\frac {1}{n}$ , 所以多问一次警察活下来的概率就会减少 $\frac {1}{n}$ , 求出询问数量除一下输出就好了OwO

然而这里有一个坑点: 如果有一个 $SCC$ 的大小仅为一个结点, 而它所连接的所有其他 $SCC$ 的入度都大于 $1$ , 那么我们就没必要问它了. 这种情况下询问次数最多可以减少 $1$(被坑N久的教训QAQ)

参考代码

GitHub

  1 #include <stack>
  2 #include <cstdio>
  3 #include <cstring>
  4 #include <cstdlib>
  5 #include <iostream>
  6 #include <algorithm>
  7 
  8 const int MAXE=3e5+10;
  9 const int MAXV=1e5+10;
 10 
 11 struct Edge{
 12     int from;
 13     int to;
 14     Edge* next;
 15 };
 16 Edge E[MAXE];
 17 Edge* head[MAXV];
 18 Edge* top=E;
 19 
 20 int v;
 21 int e;
 22 int clk;
 23 int scc;
 24 int dfn[MAXV];
 25 int low[MAXV];
 26 int size[MAXV];
 27 int belong[MAXV];
 28 int inDegree[MAXV];
 29 
 30 std::stack<int> s;
 31 
 32 bool inStack[MAXV];
 33 
 34 void Tarjan(int);
 35 void SweepEdge();
 36 void Initialize();
 37 void Insert(int,int);
 38 
 39 int main(){
 40     Initialize();
 41     for(int i=1;i<=v;i++)
 42         if(dfn[i]==0)
 43             Tarjan(i);
 44     SweepEdge();
 45     int ans=0;
 46     for(int i=1;i<=scc;i++)
 47         if(inDegree[i]==0)
 48             ans++;
 49     for(int i=1;i<=scc;i++){
 50         if(size[i]==1&&inDegree[i]==0){
 51             bool flag=true;
 52             for(Edge* k=head[i];k!=NULL;k=k->next){
 53                 if(inDegree[k->to]<=1){
 54                     flag=false;
 55                     break;
 56                 }
 57             }
 58             if(flag){
 59                 ans--;
 60                 break;
 61             }
 62         }
 63     }
 64     printf("%.6lf\n",double(v-ans)/double(v));
 65     return 0;
 66 }
 67 
 68 void Tarjan(int root){
 69     clk++;
 70     dfn[root]=low[root]=clk;
 71     s.push(root);
 72     inStack[root]=true;
 73     for(Edge* i=head[root];i!=NULL;i=i->next){
 74         if(dfn[i->to]==0){
 75             Tarjan(i->to);
 76             low[root]=std::min(low[root],low[i->to]);
 77         }
 78         else if(inStack[i->to])
 79             low[root]=std::min(low[root],dfn[i->to]);
 80     }
 81 
 82     if(low[root]==dfn[root]){
 83         scc++;
 84         int top;
 85         do{
 86             top=s.top();
 87             size[scc]++;
 88             belong[top]=scc;
 89             inStack[top]=false;
 90             s.pop();
 91         }while(top!=root);
 92     }
 93 }
 94 
 95 inline void SweepEdge(){
 96     Edge* end=top;
 97     top=E;
 98     memset(head,0,sizeof(head));
 99     for(Edge* i=E;i!=end;i++){
100         if(belong[i->from]!=belong[i->to]){
101             inDegree[belong[i->to]]++;
102             Insert(belong[i->from],belong[i->to]);
103         }
104     }
105 }
106 
107 void Initialize(){
108     int a;
109     int b;
110     scanf("%d%d",&v,&e);
111     for(int i=0;i<e;i++){
112         scanf("%d%d",&a,&b);
113         Insert(a,b);
114     }
115 }
116 
117 inline void Insert(int from,int to){
118     top->from=from;
119     top->to=to;
120     top->next=head[from];
121     head[from]=top++;
122 }
Backup

 

posted @ 2017-09-27 16:20  rvalue  阅读(344)  评论(0编辑  收藏  举报