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
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 }