[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 5Sample Output
0.800000HINT
警察只需要查证 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)
参考代码
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
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 }
本博客已弃用, 新个人主页: https://rvalue.moe, 新博客: https://blog.rvalue.moe