[bzoj2438][中山市选2011]杀人游戏

来自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;
}
posted @ 2017-05-25 20:33  FallDream  阅读(160)  评论(0编辑  收藏