来自FallDream的博客,未经允许,请勿转载,谢谢。
一位冷血的杀手潜入 Na-wiat,并假装成平民。警察希望能在 N 个人里面,查出谁是杀手。
警察能够对每一个人进行查证,假如查证的对象是平民,他会告诉警察,他认识的人, 谁是杀手, 谁是平民。 假如查证的对象是杀手, 杀手将会把警察干掉。
现在警察掌握了每一个人认识谁。 每一个人都有可能是杀手,可看作他们是杀手的概率是相同的。
问:根据最优的情况,保证警察自身安全并知道谁是杀手的概率最大是多少?
n<=100000 m<=300000
tarjan缩点之后,推一波公式,发现只要查的是入度为0的点,顺序并没有影响。
要特殊处理的是大小为1的点。当然一个联通块也有可能在查了一个人之后剩下的部分大小是1。我们可以把入度为0的点按照大小排序之后从大到小dfs,就可以找到这种情况。
答案是(n-入度为0的点的个数+是否有大小为1的点)/n
#include<iostream> #include<cstdio> #include<algorithm> #define MN 200000 using namespace std; inline int read() { int x = 0 , f = 1; char ch = getchar(); while(ch < '0' || ch > '9'){ if(ch == '-') f = -1; ch = getchar();} while(ch >= '0' && ch <= '9'){x = x * 10 + ch - '0';ch = getchar();} return x * f; } bool mark[MN+5],inq[MN+5]; int cnt=0,dn=0,cc,in[MN+5],sum=0,head[MN+5],size[MN+5],q[MN+5],top=0,bel[MN+5],n,m,dfn[MN+5],low[MN+5]; struct edge{int to,next;}e[MN*3+5]; inline void ins(int f,int t){e[++cnt]=(edge){t,head[f]};head[f]=cnt;} void tarjan(int x) { dfn[x]=low[x]=++dn;q[++top]=x;inq[x]=1; for(int i=head[x];i;i=e[i].next) if(!dfn[e[i].to]) tarjan(e[i].to),low[x]=min(low[x],low[e[i].to]); else if(inq[e[i].to]) low[x]=min(low[x],dfn[e[i].to]); if(dfn[x]==low[x]) for(++cc;q[top+1]!=x;bel[q[top]]=cc,inq[q[top]]=0,++size[cc],--top); } bool cmp(int x,int y){return size[x]>size[y];} void Dfs(int x) { sum+=size[x];mark[x]=1; for(int i=head[x];i;i=e[i].next) if(!mark[e[i].to]) Dfs(e[i].to); } int main() { cc=n=read();m=read(); for(int i=1;i<=m;++i) { int x=read(),y=read(); ins(x,y); } for(int i=1;i<=n;++i) if(!dfn[i]) tarjan(i); for(int i=1;i<=n;++i) for(int j=head[i];j;j=e[j].next) if(bel[i]!=bel[e[j].to]) ins(bel[i],bel[e[j].to]),++in[bel[e[j].to]]; for(int i=n+1;i<=cc;++i) if(!in[i]) q[++top]=i; sort(q+1,q+top+1,cmp); int ans=n-top; for(int i=1;i<=top;++i) { sum=0;Dfs(q[i]); if(sum==1) {++ans;break;} } printf("%.6lf",(double)ans/n); return 0; }