[BZOJ]2438杀人游戏[中山市选2011]

题目描述

一位冷血的杀手潜入 Na-wiat,并假装成平民。警察希望能在 N 个人里面,

查出谁是杀手。

警察能够对每一个人进行查证,假如查证的对象是平民,他会告诉警察,他认识的人, 谁是杀手, 谁是平民。假如查证的对象是杀手,杀手将会把警察干掉。

现在警察掌握了每一个人认识谁。

每一个人都有可能是杀手,可看作他们是杀手的概率是相同的。

问:根据最优的情况,保证警察自身安全并知道谁是杀手的概率最大是多少?

输入输出格式

输入格式

第一行有两个整数 N,M。

接下来有 M 行,每行两个整数 x,y,表示 x 可以续y(y 不一定可以续 x,例如贠启豪同志)。

输出格式

仅包含一行一个实数,保留小数点后面 6 位,表示最大概率。

输入输出样例

输入样例1
5 4
1 2
1 3
1 4
1 5
输出样例1
0.800000

说明

警察只需要查证 1。假如1是杀手,警察就会被杀。假如 1不是杀手,他会告诉警察 2,3,4,5 谁是杀手。而 1 是杀手的概率是 0.2,所以能知道谁是杀手但没被杀的概率是0.8。

对于 100%的数据有 $$1 \leq N \leq 10^6 , 0 \leq M \leq 3 \cdot 10^6$$,数据有梯度

思路

强连通分量缩点

因为询问一个人只有两种可能,一种此人是杀手,一种此人不是。所以概率即为:$$1- \frac{询问人数}{总人数}$$

那么要求最大概率,就要使询问人数最少。这个时候思路就很明显了,先tarjan缩点,然后求出入度为0的点的个数(必须要询问)。

这个时候还有一种特殊情况,某个入度为0的点,它所连向的所有点得入度都>1,即我们确实是不需要询问这个点就可以知道答案的,所以就减去即可。

代码

#include<iostream>
#include<cstdio>
#include<cstring>
#include<vector>
#include<algorithm>
#include<map>
using namespace std;

int n,m,scc,cnt,ans,ind,top;
int head[100005],head2[100005],d[100005];
int low[100005],dfn[100005],st[100005],IC[100005],num[100005];
bool inst[100005],vis[100005];
struct node{int to,next,v;};
node e[300005],ed[300005];

int read()
{
    int sum=0,flag=1;char ch=getchar();
    while(ch>'9' || ch<'0'){if(ch=='-')flag=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){sum=sum*10+ch-'0';ch=getchar();}
    return sum*flag;
}

void add_edge(int u,int v)
{
    e[++cnt].to=v;e[cnt].next=head[u];head[u]=cnt;
}

void add_newedge(int u,int v)
{
    d[v]++;
    ed[++cnt].to=v;ed[cnt].next=head2[u];head2[u]=cnt;
}

void Tarjan(int x)
{
    low[x]=dfn[x]=++ind;
       inst[x]=1;
    st[++top]=x;
    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(vis[e[i].to])
            low[x]=min(low[x],dfn[e[i].to]);
    if(low[x]==dfn[x])
    {
        int now=0;
        scc++;
        while(x!=now)
        {
            now=st[top];top--;
            IC[now]=scc;
            inst[now]=0;
            num[scc]++;
        } 
    }
}

void rebuild()
{
    cnt=0;
    for(int x=1;x<=n;x++)
    {
        for(int i=head[x];i;i=e[i].next)
            if(IC[x]!=IC[e[i].to]&&!vis[IC[e[i].to]])
                vis[IC[e[i].to]]=1,add_newedge(IC[x],IC[e[i].to]);
        for(int i=head[x];i;i=e[i].next)
            if(IC[x]!=IC[e[i].to])
                vis[IC[e[i].to]]=0;
    }
}

bool judge(int x)
{
    if(d[x]!=0||num[x]!=1)    return 0;
    for(int i=head2[x];i;i=ed[i].next)
        if(d[ed[i].to]==1)    return 0;
    return 1;
}

int main()
{

    n=read();m=read();
    for(int i=1;i<=m;i++)
    {
        int u=read(),v=read();
        add_edge(u,v);
    }
    for(int i=1;i<=n;i++)
        if(!dfn[i])    Tarjan(i);
    rebuild();
    for(int i=1;i<=scc;i++)
        if(!d[i])    ans++;
    for(int i=1;i<=scc;i++)
        if(judge(i))
        {
            ans--;
            break;
        }
    printf("%.6lf",(double)(n-ans)/n);
}

results matching ""

    No results matching ""