【中山市选2011】杀人游戏

一位冷血的杀手潜入Na-wiat ,并假装成平民。警察希望能在N个人里面查出谁是杀手
警察能够对每一个人进行查证,假如查证的对象是平民,他会告诉警察,他认识的人,谁是杀手,谁是平民
假如查证的对象是杀手,杀手将会把警察干掉
现在警察掌握了每一一个人认识谁。 每一一个人都有可能是杀手,看作他们是杀手的概率是相同的
根据最优的情况,保证警察自身安全并知道谁是杀手的概率最大是多少?

每个人的认识关系都可以看作一条有向边
而如果这些有向的关系变成了一个环,我们就可以缩点了——
缩点后,调查任意一个在同一个强联通分量中的人都可以知道这个分量中所有人的身份

另外,对于一个有脑子的Police,如果已经调查了除 \(i\) 之外的所有人都不是凶手,\(i\) 一定是凶手
遇到这种情况,我们就不需要调查了

于是我们判断一下是否有这种谁都不认识的人存在
若存在,即可少调查一次。设缩点后的起始点(入度为0)的个数为x

\[ans=1-\dfrac{1-x+(flag==1)}{n}\]

代码:

#include
#define N 100005
#define M 300005
using namespace std;

int n,m,u[M],v[M];
bool flag;

struct Edge
{
    int next,to;
}edge[M<<1];
int cnt=0,head[N];

inline void add_edge(int from,int to)
{
    edge[++cnt].next=head[from];
    edge[cnt].to=to;
    head[from]=cnt;
}

templateinline void read(T &res)
{
    char c;T flag=1;
    while((c=getchar())<'0'||c>'9')if(c=='-')flag=-1;res=c-'0';
    while((c=getchar())>='0'&&c<='9')res=res*10+c-'0';res*=flag;
}

int low[N],dfn[N],tms=0,co[N],col=0,sum[N];
stack sta;
void tarjan(int u)
{
    low[u]=dfn[u]=++tms;
    sta.push(u);
    for(register int i=head[u];i;i=edge[i].next)
    {
        int v=edge[i].to;
        if(!dfn[v])
        {
            tarjan(v);
            low[u]=min(low[u],low[v]);
        }
        else if(!co[v]) low[u]=min(low[u],dfn[v]);
    }
    if(low[u]==dfn[u])
    {
        co[u]=++col;
        sum[col]++;
        while(sta.top()!=u)
        {
            co[sta.top()]=col;
            sum[col]++;
            sta.pop();
        }
        sta.pop();
    }
}

int rd[N];
double ans;
int main()
{
    read(n);read(m);
    for(register int i=1;i<=m;++i)
    {
        read(u[i]);read(v[i]);
        add_edge(u[i],v[i]);
    }
    for(register int i=1;i<=n;++i) if(!dfn[i]) tarjan(i);
    cnt=0;
    memset(head,0,sizeof(head));
    for(register int i=1;i<=m;++i)
    {
        if(co[u[i]]==co[v[i]]) continue;
        rd[co[v[i]]]++;
        add_edge(co[u[i]],co[v[i]]);
    }
    for(register int u=1;u<=col;++u)
    {
        if(!flag&&!rd[u]&&sum[u]==1)
        {
            bool yes=0;
            for(register int i=head[u];i;i=edge[i].next)
            {
                int v=edge[i].to;
                if(rd[v]==1) yes=1;
            }
            if(!yes) flag=1;
        }
        if(!rd[u]) ans++;
    }
    if(flag) ans--;
    printf("%.6f\n",1.0-(double)ans/(double)n);
}

你可能感兴趣的:(【中山市选2011】杀人游戏)