题目大意:有n个人,其中一个是杀手,可以询问一些人,如果是杀手就会死,如果是平民,他会告诉你他认识的人中有谁是杀手有谁是平民,求自身安全并知道杀手的概率最大是多少
题解:某个人是杀手的概率相等。考虑到每个SCC只问一个点就可以,进行缩点,缩点后每个SCC对答案贡献等价。
知道谁是杀手相当于知道所有人的身份,需要在DAG中选择x个点,使得至少能够遍历n-1个点(确定n-1就可以确定全部),要求x最小,那么显然x取所有入度为0的点,答案即为
(n−x)÷n
如果某个SCC满足入度为0的条件,但是其大小为1,且这个SCC的所有出边到达的点入度均大于1,那么这个点就可以不选(即上文中的n-1情况),图中只能有1个这样的点
我的收获:朴素算概率,讨论
#include <iostream>
#include <cstdio>
#include <cstring>
#include <iomanip>
#include <algorithm>
using namespace std;
#define M 100005
int tm,scnt,top;
int n,m,t,t2,ans;
int head[M],last[M];
int low[M],dfn[M],col[M],sz[M],in[M],s[M*5];
bool ins[M],mark[M];
struct edge{int to,nex;}e[M*5],p[M*5];
void add(int u,int v){e[t].to=v,e[t].nex=head[u],head[u]=t++;}
void insert(int u,int v){p[t2].to=v,p[t2].nex=last[u],last[u]=t2++;}
void tarjan(int x){
int now=0;
dfn[x]=low[x]=++tm;
s[++top]=x;ins[x]=true;
for(int i=head[x];i!=-1;i=e[i].nex){
int v=e[i].to;
if(!dfn[v]) tarjan(v),low[x]=min(low[x],low[v]);
else if(ins[v]&&dfn[v]<low[x]) low[x]=dfn[v];
}
if(low[x]==dfn[x]){
scnt++;
while(x!=now) now=s[top--],ins[now]=false,col[now]=scnt,sz[scnt]++;
}
}
void rebuild(){
for(int x=1;x<=n;x++){
for(int i=head[x];i!=-1;i=e[i].nex){
int v=e[i].to;
if(col[x]!=col[v]&&!mark[col[v]])
mark[col[v]]=1,in[col[v]]++,insert(col[x],col[v]);
}
for(int i=head[x];i!=-1;i=e[i].nex)
if(col[x]!=col[e[i].to]) mark[col[e[i].to]]=0;
}
}
bool check(int x){
for(int i=last[x];i!=-1;i=p[i].nex) if(in[p[i].to]==1) return false;
return true;
}
void work()
{
for(int i=1;i<=n;i++) if(!dfn[i]) tarjan(i);
rebuild();
for(int i=1;i<=scnt;i++) if(!in[i]) ans++;
for(int x=1;x<=scnt;x++) if(sz[x]==1&&!in[x]&&check(x)) {ans--;break;}
cout<<fixed<<setprecision(6)<<1.0-(double)ans/n<<endl;
}
void init()
{
int x,y;t=0;memset(head,-1,sizeof(head));
t2=0;memset(last,-1,sizeof(last));
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++) scanf("%d%d",&x,&y),add(x,y);
}
int main()
{
init();
work();
return 0;
}