首页 > 代码库 > BZOJ 2438 中山市选 2011 杀人游戏 Tarjan

BZOJ 2438 中山市选 2011 杀人游戏 Tarjan

题目大意:给出一张有向人物关系图,告诉你谁认识谁,认识具有传递性。其中有一个人是犯人。现在警察要调查谁是犯人。他可以问任何人。但是如果他问到了犯人,那么它就会死。如果他问到的一个人认识犯人,这个人就会告诉警察谁是犯人。问警察保证自身安全并知道犯人是谁的概率最大是多少。


思路:这个题前一阵子重测了,加强了数据,卡掉了网上一片AC代码。。

正解并不是很难想。首先先缩点,整个图变成拓扑图,之后会出现一些类似根的东西,这些scc入度为0,只要警察询问了这些scc每一个中的任意一个,就肯定能知道谁是犯人。安全程度就简单算一下。

但是会有特殊情况。比如只有一个人的情况,警察不用询问就可以知道他肯定是犯人。

类似的,如果在整个图中有一个scc的size为1并且这个scc的出边所到达的scc的入度全部大于1,那么就不用询问这个点,询问所有其他的“根”就能推出这个人是不是犯人。

CODE:

#include <cstdio>
#include <cstring>
#include <iomanip>
#include <iostream>
#include <algorithm>
#define MAX 3000010
using namespace std;
#define min(a,b) ((a) < (b) ? (a):(b))
#define max(a,b) ((a) > (b) ? (a):(b))
 
int points,edges;
int head[MAX],total;
int next[MAX],aim[MAX];
 
inline void Add(int x,int y)
{
    next[++total] = head[x];
    aim[total] = y;
    head[x] = total;
}
 
int dfn[MAX],low[MAX],_clock;
int stack[MAX],top;
bool in_stack[MAX];
int changed[MAX],scc,cnt[MAX];
 
void Tarjan(int x)
{
    dfn[x] = low[x] = ++_clock;
    stack[++top] = x;
    in_stack[x] = true;
    for(int i = head[x]; i; i = next[i]) {
        if(!dfn[aim[i]])
            Tarjan(aim[i]),low[x] = min(low[x],low[aim[i]]);
        else if(in_stack[aim[i]])
            low[x] = min(low[x],dfn[aim[i]]);
    }
    if(low[x] == dfn[x]) {
        scc++;
        int temp;
        do {
            temp = stack[top--];
            in_stack[temp] = false;
            changed[temp] = scc;
            ++cnt[scc];
        }while(temp != x);
    }
}
 
int _in[MAX];
 
int main()
{
    cin >> points >> edges;
    for(int x,y,i = 1; i <= edges; ++i) {
        scanf("%d%d",&x,&y);
        Add(x,y);
    }
    for(int i = 1; i <= points; ++i)
        if(!dfn[i]) Tarjan(i);
    for(int x = 1; x <= points; ++x)
        for(int i = head[x]; i; i = next[i])
            if(changed[x] != changed[aim[i]])
                ++_in[changed[aim[i]]];
    int ans = 0;
    for(int i = 1; i <= scc; ++i)
        ans += !_in[i];
    bool flag = false;
    for(int x = 1; x <= points; ++x) {
        if(cnt[changed[x]] != 1 || _in[changed[x]])    continue;
        bool temp = true;
        for(int i = head[x]; i; i = next[i])
            if(_in[changed[aim[i]]] == 1) {
                temp = false;
                break;
            }
        if(temp) {
            flag = true;
            break;
        }
    }
    ans -= flag;
    cout << fixed << setprecision(6) << 1.0 - (double)ans / points << endl;
    return 0;
}


BZOJ 2438 中山市选 2011 杀人游戏 Tarjan