[中山市选2011]杀人游戏

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

Online Judge:Bzoj-2438,Luogu-4819

Label:Tarjan缩点,概率,Yy

题目描述

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

进行查证,假如查证的对象是平民,他会告诉警察,他认识的人, 谁是杀手, 谁是平民。 假如查证的对象是杀手, 杀手将会把警察干掉。现在警察掌握了每一个人认识谁。每一个人都有可能是杀手,可看作他们是杀手的概率是相同的。问:根据最优的情况,保证警察自身安全并知道谁是杀手的概率最大是多少

输入格式

第一行有两个整数 N,M。
接下来有 M 行,每行两个整数 x,y,表示 x 认识 y(y 不一定认识 x,例如hjt同志) 。——由于zz敏感博客园里不能放原题面233

输出格式

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

样例

输入

5 4 
1 2 
1 3 
1 4 
1 5 

输出

0.800000 

题解:

仔细Yy一下,发现这样一个性质:对于一个环,只要查证其中任何一个人就可以知道这个环里所有人的信息。也就是说可以把整个环看做一个整体,由此想到Tarjan缩点,然后建立一个强联通分量与另一个之间的关系(以下为了方便用"团"代替强连通分量)。

那么最后可以得到入度为0的团的数目为ans个,设这些团构成集合S,则对与任意团∈S,只要在这个团里任意选一个点查证就可以知道整个团的信息了。查证完这ans个团后,存活概率为\(1-ans/n\)(凶手不在我们查的那ans个人里,我们就可以存活)。而只要存活就能找到最终的凶手!所以答案就是存活的概率。

但是!我们发现题目还有一个特殊情况,比如当\(n=1,m=0\)时我们不用查也可以知道那唯一那个人是凶手,也就是说,当我们知道n-1人不是凶手后,剩下的那个肯定是凶手,我们不必要再冒险去查剩余的那个人,所以得特殊处理这种情况。

我们将这种情况形象化到上面的模型中去。设该强连通分量为x,则x需满足:

  1. 不能从别人那直接得到他的信息——x入度为0
  2. 如果x认识p,则至少还得存在一个人认识p(不然不查证x就不能知道p)——p的入度不为1
  3. x这个强连通分量大小为1

另外由于可能存在多个满足上述条件的强连通分量x,而我们只能选一个 。可以联系n=2,m=0的样例进行理解,答案应该是\(0.500000\).

代码如下,主要思路就是Tarjan缩点,然后特殊情况特殊处理一下。

#include<bits/stdc++.h>
using namespace std;
const int N=1e5+5;
stack<int>s;
vector<int>e[N],v[N];
int tot,cnt,n,m;
int dfn[N],low[N],in[N],sz[N],id[N];
int ing[N];
void tarjan(int x){
    dfn[x]=low[x]=++tot;
    s.push(x),in[x]=1;
    for(int i=0;i<e[x].size();i++){
        int y=e[x][i];
        if(!dfn[y]){
            tarjan(y);
            low[x]=min(low[x],low[y]);
        }
        else if(in[y])low[x]=min(low[x],dfn[y]);
    }
    int k;
    if(low[x]==dfn[x]){
        cnt++;
        do{
            k=s.top();s.pop();
            in[k]=0,id[k]=cnt,sz[cnt]++;
        }while(x!=k);
    }
}
bool check(int x){
    for(int i=0;i<v[x].size();i++){
        if(ing[v[x][i]]==1)return 0;
    }
    return 1;
}
int main(){
    scanf("%d%d",&n,&m);
    for(int i=1,u,v;i<=m;i++){
        scanf("%d%d",&u,&v);
        e[u].push_back(v);
    }
    for(int i=1;i<=n;i++)if(!dfn[i])tarjan(i);
    for(int i=1;i<=n;i++)for(int j=0;j<e[i].size();j++){
        int y=e[i][j];
        if(id[i]!=id[y])v[id[i]].push_back(id[y]),ing[id[y]]++;
    }
    int ans=0,find=0;
    for(int i=1;i<=cnt;i++){
        if(!find&&!ing[i]&&sz[i]==1&&check(i))find=1;
        if(!ing[i])ans++;
    }
     
    if(find)ans--;
    printf("%.6f",1.0-1.0*ans/n);
}
posted @ 2019-07-18 16:37  real_lyb  阅读(251)  评论(0编辑  收藏  举报