山东济南彤昌机械科技有限公司 山东济南江鹏工贸游有限公司

bzoj 2438 [中山市选2011]杀人游戏(SCC+概率)

 

【题目链接】

 

    http://www.lydsy.com/JudgeOnline/problem.php?id=2438

 

【题意】

 

    N个人中有一个杀手,每次询问一个人可能被杀或被告知其认识的人中谁是杀手谁是平民,问不被杀的情况下知道谁是杀手的概率。

 

【思路】

 

    对于一个scc,如果我们询问一个不是杀手的人,就可以成功遍历整个scc。

    求scc,然后缩点。

    对于每一个入度为0的scc,我们需要去询问一下,那么被杀的概率为1/n * ans。ans为入度为0的scc个数。

    但还有一种特殊的情况,如果一个scc入度为0,但它的所有出度的入度皆大于1,那么它的出度可以不通过询问这个scc得知,而且最终通过其他n-1个点推出这个点的情况。这时候需要对ans-1,但这种情况只计算一次。

 

【代码】

 

 1 #include<set>
 2 #include<cmath>
 3 #include<queue>
 4 #include<vector>
 5 #include<cstdio>
 6 #include<cstring>
 7 #include<iostream>
 8 #include<algorithm>
 9 #define trav(u,i) for(int i=front[u];i;i=e[i].nxt)
10 #define FOR(a,b,c) for(int a=(b);a<=(c);a++)
11 using namespace std;
12 
13 typedef long long ll;
14 const int N = 2e5+10;
15 const int M = 7e5+10;
16 
17 ll read() {
18     char c=getchar();
19     ll f=1,x=0;
20     while(!isdigit(c)) {
21         if(c=='-') f=-1; c=getchar();
22     }
23     while(isdigit(c))
24         x=x*10+c-'0',c=getchar();
25     return x*f;
26 }
27 
28 struct Edge {
29     int v,nxt;
30 }e[M];
31 int en=1,front[N];
32 void adde(int u,int v)
33 {
34     e[++en]=(Edge){v,front[u]}; front[u]=en;
35 }
36 
37 int pre[N],lowlink[N],scc_cnt,sccno[N],S[N],top,dfn;
38 
39 void tarjan(int u)
40 {
41     pre[u]=lowlink[u]=++dfn;
42     S[++top]=u;
43     trav(u,i) {
44         int v=e[i].v;
45         if(!pre[v]) {
46             tarjan(v);
47             lowlink[u]=min(lowlink[v],lowlink[u]);
48         } else
49         if(!sccno[v])
50             lowlink[u]=min(lowlink[u],pre[v]);
51     }
52     if(pre[u]==lowlink[u]) {
53         ++scc_cnt;
54         for(;;) {
55             int x=S[top--];
56             sccno[x]=scc_cnt;
57             if(x==u) break;
58         }
59     }
60 }
61 
62 int cnt[N],in[N],n,m,ans;
63 
64 int main()
65 {
66     n=read(),m=read();
67     int u,v;
68     FOR(i,1,m) {
69         u=read(),v=read();
70         adde(u,v);
71     }
72     FOR(i,1,n) if(!pre[i]) tarjan(i);
73     FOR(u,1,n) {
74         cnt[sccno[u]]++;
75         trav(u,i) {
76             int v=e[i].v;
77             if(sccno[u]!=sccno[v]) 
78                 in[sccno[v]]++;
79         }
80     }
81     FOR(i,1,scc_cnt) if(!in[i]) ans++;
82     int flag=0;
83     FOR(i,1,n) 
84         if(cnt[sccno[i]]==1&&in[sccno[i]]==0) {
85             int f=1;
86             trav(i,j) {
87                 int v=e[j].v;
88                 if(in[sccno[v]]<=1) { f=0; break; } 
89             }
90             if(f) {
91                 flag=1; break;
92             }
93         }
94     ans-=flag;
95     printf("%.6lf\n",(double)(n-ans)/n);
96     return 0;
97 }

 

posted on 2016-03-30 08:53  hahalidaxin  阅读(196)  评论(0编辑  收藏