[中山OI2011]杀人游戏 题解

[中山OI2011]杀人游戏 题解

题目地址:BZOJ:Problem 2438. — [中山市选2011]杀人游戏

题目描述

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

题意简述

有$n$个人,其中一个人是杀手,警察知道了每个人认识的人的情况,询问一个人,如果他是平民,则会告诉警察他认识的人的身份,如果是杀手警察会被杀死。现在每个人是平民或杀手的概率是相等的,求在最优情况下,警察安全且得到答案的概率。

输入输出格式

输入格式:
第一行有两个整数 N,M。
接下来有 M 行,每行两个整数 x,y,表示 x 认识 y(y 不一定认识 x,例如[一个宽为$w$高为$h$的矩形的面积])。

输出格式:
仅包含一行一个实数,保留小数点后面 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≤N ≤ 10 0000,0≤M ≤ 30 0000

题解

首先,对于一个强连通分量里的点,只需要询问分量中一个不是杀手的人,那么这个分量及它连接的分量的信息都可以安全地知道,因此我们可以考虑缩点然后统计入度为0的点有多少个。
然后你就WA了,这是因为,如果$n-1$个点的信息都知道了,那么第$n$个点不用询问也可以知道,可以少冒一次险问一个未知的人。满足这样条件的点一定是在缩点后的新图中大小为1,入度为0且连接的分量入度都不小于2的这样的分量。
那么假如最后求出需要冒险询问的人数是$x$,那么概率就是$\frac{n-x}{n}$。
复杂度$O(n+m)$。

代码

// Code by KSkun, 2018/7
#include <cstdio>
#include <cctype>
#include <cstring>

#include <algorithm>
#include <vector>
#include <stack>
#include <set>

typedef long long LL;

inline char fgc() {
    static char buf[100000], *p1 = buf, *p2 = buf;
    return p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 100000, stdin), p1 == p2)
        ? EOF : *p1++;
}

inline LL readint() {
    register LL res = 0, neg = 1; register char c = fgc();
    for(; !isdigit(c); c = fgc()) if(c == '-') neg = -1;
    for(; isdigit(c); c = fgc()) res = res * 10 + c - '0';
    return res * neg;
}

const int MAXN = 100005;

int n, m;

std::vector<int> gra[MAXN], gran[MAXN];
int deg[MAXN];

int dfn[MAXN], low[MAXN], clk, sno[MAXN], ssiz[MAXN], scc;
std::stack<int> sta;
bool insta[MAXN];

void tarjan(int u) {
    dfn[u] = low[u] = ++clk;
    sta.push(u); insta[u] = true;
    for(int i = 0; i < gra[u].size(); i++) {
        int v = gra[u][i];
        if(!dfn[v]) {
            tarjan(v);
            low[u] = std::min(low[u], low[v]);
        } else if(insta[v]) {
            low[u] = std::min(low[u], dfn[v]);
        }
    }
    if(low[u] == dfn[u]) {
        int p; scc++;
        do {
            p = sta.top(); sta.pop();
            insta[p] = false;
            sno[p] = scc;
            ssiz[scc]++;
        } while(p != u);
    }
}

typedef std::pair<int, int> PII;
std::set<PII> edges;

inline bool check(int u) {
    if(deg[u] != 0 || ssiz[u] != 1) return false;
    for(int i = 0; i < gran[u].size(); i++) {
        int v = gran[u][i];
        if(deg[v] < 2) return false;
    }
    return true;
}

int main() {
    n = readint(); m = readint();
    for(int i = 1, u, v; i <= m; i++) {
        u = readint(); v = readint();
        gra[u].push_back(v);
    }
    for(int i = 1; i <= n; i++) {
        if(!dfn[i]) tarjan(i);
    }
    for(int u = 1; u <= n; u++) {
        for(int i = 0; i < gra[u].size(); i++) {
            int v = gra[u][i];
            if(sno[u] != sno[v] && !edges.count(PII(sno[u], sno[v]))) {
                gran[sno[u]].push_back(sno[v]);
                deg[sno[v]]++;
                edges.insert(PII(sno[u], sno[v]));
            }
        }
    }
    int cnt = 0;
    for(int i = 1; i <= scc; i++) {
        if(!deg[i]) cnt++;
    }
    for(int i = 1; i <= scc; i++) {
        if(check(i)) {
            cnt--; break;
        }
    }
    printf("%.6lf", double(n - cnt) / n);
    return 0;
}


发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

This site is protected by reCAPTCHA and the Google Privacy Policy and Terms of Service apply.

此站点使用Akismet来减少垃圾评论。了解我们如何处理您的评论数据