Description
一位冷血的杀手潜入 Na-wiat,并假装成平民。警察希望能在 N 个人里面,查出谁是杀手。警察能够对每一个人
进行查证,假如查证的对象是平民,他会告诉警察,他认识的人, 谁是杀手, 谁是平民。 假如查证的对象是杀
手, 杀手将会把警察干掉。现在警察掌握了每一个人认识谁。每一个人都有可能是杀手,可看作他们是杀手的概
率是相同的。问:根据最优的情况,保证警察自身安全并知道谁是杀手的概率最大是多少?
对于 100%的数据有 1≤N ≤ 10 0000,0≤M ≤ 30 0000
Solution
注意到给出的是有向边,若图为DAG显然我们选择拓扑序更小的点更优秀,放在有向图上就是选择缩点后入度为0的强连通分量最优秀
需要注意的是,对于像3 0这样的数据答案为0.333333,也即是我们可以通过排除法问完n-1个人后剩下的人身份就确定了,这个需要特判一下
Code
#include <stdio.h>
#include <string.h>
#include <algorithm>
#include <stack>
#define rep(i,st,ed) for (int i=st;i<=ed;++i)
#define fill(x,t) memset(x,t,sizeof(x))
const int N=100005;
struct Graph {
struct edge {int x,y,next;} e[N*3];
int ls[N],edCnt;
void add_edge(int x,int y) {
e[++edCnt]=(edge) {x,y,ls[x]}; ls[x]=edCnt;
}
} E,G;
std:: stack <int> stack;
int dfn[N],low[N],scc[N],ind[N],size[N];
bool vis[N],flag;
int read() {
int x=0,v=1; char ch=getchar();
for (;ch<'0'||ch>'9';v=(ch=='-')?(-1):(v),ch=getchar());
for (;ch<='9'&&ch>='0';x=x*10+ch-'0',ch=getchar());
return x*v;
}
void dfs(int now) {
dfn[now]=low[now]=++dfn[0];
vis[now]=true; stack.push(now);
for (int i=E.ls[now];i;i=E.e[i].next) {
if (!dfn[E.e[i].y]) {
dfs(E.e[i].y); low[now]=std:: min(low[now],low[E.e[i].y]);
} else if (vis[E.e[i].y]) {
low[now]=std:: min(low[now],dfn[E.e[i].y]);
}
}
if (low[now]==dfn[now]) {
int y=0; scc[0]++;
while (y!=now) {
y=stack.top(); stack.pop();
scc[y]=scc[0]; vis[y]=0; size[scc[0]]++;
}
}
}
bool check(int x) {
if (flag) return false;
if (size[x]!=1) return false;
for (int i=G.ls[x];i;i=G.e[i].next) {
if (ind[G.e[i].y]==1) return false;
}
return flag=true;
}
int main(void) {
int n=read(),m=read();
rep(i,1,m) {
int x=read(),y=read();
E.add_edge(x,y);
}
rep(i,1,n) if (!dfn[i]) dfs(i);
rep(i,1,E.edCnt) {
if (scc[E.e[i].x]!=scc[E.e[i].y]) {
G.add_edge(scc[E.e[i].x],scc[E.e[i].y]);
ind[scc[E.e[i].y]]++;
}
}
int ans=0;
rep(i,1,scc[0]) if (!ind[i]) {
if (check(i)) continue;
ans++;
}
printf("%.6lf\n", 1.0*(n-ans)/n);
return 0;
}