【传送门:BZOJ2438】
简要题意:
有n个人,其中有一个人是杀手,现在作为警察的你要去找到杀手,那么你就要知道每个人的身份
知道一个人的身份可以直接询问这个人,或者询问认识这个人的人
如果当前询问的人是杀手,那么你就死了,如果不是,那么这个人会告诉你他认识的所有人的身份
请问找出杀手并保证自身安全的最大概率
题解:
一场模拟赛出了这道题,一开始只知道要缩点,但仍不会做
赛后讲完题才会做
缩点后,ans=入度为0的连通块个数
倘若存在只有一个点的连通块,它无入度并且它连向的点均能被其它点到达则ans--
参考代码:
#include<cstdio> #include<cstring> #include<cstdlib> #include<cmath> #include<algorithm> using namespace std; int n,m; struct node { int x,y,next; }a[1110000];int len,last[510000]; void ins(int x,int y) { len++; a[len].x=x;a[len].y=y; a[len].next=last[x];last[x]=len; } struct edge { int x,y,next; }e[1110000];int len1,last1[510000]; void ins1(int x,int y) { len1++; e[len1].x=x;e[len1].y=y; e[len1].next=last1[x];last1[x]=len1; } int cnt,tp,id; int belong[510000],dfn[510000],low[510000],sta[510000],size[510000]; bool v[510000]; void dfs(int x) { low[x]=dfn[x]=++id; sta[++tp]=x;v[x]=true; for(int k=last[x];k;k=a[k].next) { int y=a[k].y; if(dfn[y]==-1) { dfs(y); low[x]=min(low[x],low[y]); } else { if(v[y]==true) low[x]=min(low[x],dfn[y]); } } if(low[x]==dfn[x]) { int i;cnt++; do { i=sta[tp--]; v[i]=false; belong[i]=cnt; size[cnt]++; }while(i!=x); } } int ru[510000],chu[510000],sum1,sum2; bool check(int x) { if(size[x]!=1)return true; if(last1[x]==0)return false; for(int k=last1[x];k;k=e[k].next) { int y=e[k].y; if(ru[y]<=1)return true; } return false; } int main() { cnt=tp=id=sum1=sum2=0; len=0;len1=0; memset(last,0,sizeof(last));memset(last1,0,sizeof(last1)); scanf("%d%d",&n,&m); memset(chu,0,sizeof(chu)); memset(ru,0,sizeof(ru)); memset(sta,0,sizeof(sta)); memset(dfn,-1,sizeof(dfn)); memset(low,0,sizeof(low)); memset(v,false,sizeof(v)); for(int i=1;i<=m;i++) { int x,y; scanf("%d%d",&x,&y); ins(x,y); } for(int i=1;i<=n;i++) if(dfn[i]==-1) dfs(i); for(int i=1;i<=m;i++) { int st=belong[a[i].x],ed=belong[a[i].y]; if(st!=ed) { ru[ed]++; ins1(st,ed); } } for(int i=1;i<=cnt;i++) if(ru[i]==0) sum1++; for(int i=1;i<=cnt;i++) if(ru[i]==0) if(check(i)==false){sum1--;break;} printf("%.6lf\n",1.0-double(sum1)/double(n)); return 0; }