1. 程式人生 > >bzoj2438 殺人遊戲 Tarjan強聯通

bzoj2438 殺人遊戲 Tarjan強聯通

std main 現在 bzoj 一位 content break bzoj2438 小數點

【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

題解

在一個強聯通中,可以一步一步推出每個人,所以只需要知道一個人就可以了,然後就是入度為0的點有關了。

ans=入度為0的連通塊個數

 1 #include<cstdio>
 2 #include<algorithm>
 3 using namespace std;
 4 const int maxn=1e5+5,maxm=3e5+5;
 5 
 6 int pre[maxn],low[maxn],scc[maxn],clock,cnt;
 7 int head[maxn],f[maxm],e[maxm],nxt[maxm],k;
 8 int adde(int u,int v){
 9     e[++k]=v,f[k]=u;
10     nxt[k]=head[u],head[u]=k;
11 } 12 int n,m,r[maxn],a[maxn],t; 13 int size[maxn],num[maxn]; 14 15 int dfs(int u){ 16 pre[u]=low[u]=++clock; 17 a[++t]=u; 18 for(int i=head[u];i;i=nxt[i]){ 19 int v=e[i]; 20 if(!pre[v]){ 21 dfs(v); 22 low[u]=min(low[u],low[v]); 23 } 24 else if(!scc[v]){ 25 low[u]=min(low[u],pre[v]); 26 } 27 } 28 if(low[u]==pre[u]){ 29 num[++cnt]=u; 30 while(t){ 31 scc[a[t]]=cnt; 32 size[cnt]++; 33 if(a[t--]==u) break; 34 } 35 } 36 } 37 38 int pd(int x){ 39 int u=num[x]; 40 for(int i=head[u];i;i=nxt[i]) 41 if(r[scc[e[i]]]==1) return 0; 42 return 1; 43 } 44 45 int main(){ 46 scanf("%d%d",&n,&m); 47 int u,v; 48 for(int i=1;i<=m;i++){ 49 scanf("%d%d",&u,&v); 50 adde(u,v); 51 } 52 53 for(int i=1;i<=n;i++) 54 if(!pre[i]) dfs(i); 55 56 for(int i=1;i<=k;i++) 57 if(scc[f[i]]!=scc[e[i]]) r[scc[e[i]]]++; 58 59 int ans=0; 60 for(int i=1;i<=cnt;i++) 61 if(!r[i]) ans++; 62 63 for(int i=1;i<=cnt;i++) 64 if(size[i]==1&&!r[i]&&pd(i)){ 65 ans--; 66 break; 67 } 68 69 printf("%.6lf",(double)(n-ans)/n); 70 return 0; 71 }

bzoj2438 殺人遊戲 Tarjan強聯通