2438: [中山市选2011]杀人游戏 tarjan+概率与期望

这是一道好题啊。
首先想这样一个问题,如果存在一个环,我们只需要问其中一个人,若它不是杀手,那么我们便知道了环内所有人的身份(根据最优的情况),因此可以先tarjan缩一下环。
然后,对于每一个入度为0的强连通分量,我们都是要去询问的。所以ans=入度为0的强连通分量的个数。
存在一种特殊情况:入度为零,大小为1的强连通分量,且这个单点的所有出边指向的点所在的强连通分量 入度都大于等于2,及它指向的所有点都能被其他点访问到。这时我们便无需访问这个点,因为通过访问其他点我们就可以知道其他n-1个人的身份,而剩下一个排除就好了。

#include<iostream>
#include<cstdio>
#include<cstring>
#define N 100005
using namespace std;
int n,m,scc,cnt,cnt0,ans,top,tot;
int head[N],head0[N],ind[N];
int low[N],dfn[N],stack[N],belong[N],num[N];
bool inset[N],v[N];
int next[300005],next0[300005],list[300005],list0[300005];
inline int read()
{
    int a=0,f=1; char c=getchar();
    while (c<'0'||c>'9') {if (c=='-') f=-1; c=getchar();}
    while (c>='0'&&c<='9') {a=a*10+c-'0'; c=getchar();}
    return a*f;
}
inline void insert(int x,int y)
{
    next[++cnt]=head[x];
    head[x]=cnt;
    list[cnt]=y;
}
inline void insert0(int x,int y)
{
    next0[++cnt0]=head0[x];
    head0[x]=cnt0;
    list0[cnt0]=y;
    ind[y]++;
}
void dfs(int x)
{
    dfn[x]=low[x]=++tot;
    inset[x]=1;
    stack[++top]=x;
    for (int i=head[x];i;i=next[i])
        if (!dfn[list[i]])
        {
            dfs(list[i]);
            low[x]=min(low[x],low[list[i]]);
        }
        else if (inset[list[i]]) low[x]=min(low[x],dfn[list[i]]);
    if (dfn[x]==low[x])
    {
        int i=-1; scc++;
        while (i!=x)
        {
            i=stack[top--];
            belong[i]=scc;
            inset[i]=0;
            num[scc]++;
        }
    }
}
inline void tarjan()
{
    for (int i=1;i<=n;i++) if (!dfn[i]) dfs(i);
}
inline void rebuild()
{
    for (int x=1;x<=n;x++)
    {
        for (int i=head[x];i;i=next[i])
            if (belong[x]!=belong[list[i]]&&!v[belong[list[i]]])
                v[belong[list[i]]]=1,insert0(belong[x],belong[list[i]]);
        for (int i=head[x];i;i=next[i])
            if (belong[x]!=belong[list[i]]) v[belong[list[i]]]=0;
    }
}
inline bool judge(int x)
{
    if (ind[x]!=0||num[x]!=1) return 0;
    for (int i=head0[x];i;i=next0[i])
        if (ind[list0[i]]==1) return 0;
    return 1;
}
int main()
{
    n=read(); m=read();
    for (int i=1;i<=m;i++)
    {
        int u=read(),v=read();
        insert(u,v);
    }
    tarjan();
    rebuild();
    for (int i=1;i<=scc;i++) 
        if (!ind[i]) ans++;
    for (int i=1;i<=scc;i++) 
        if (judge(i)) {ans--; break;}
    printf("%.6lf",(double)(n-ans)/n);
    return 0;
}

你可能感兴趣的:(2438: [中山市选2011]杀人游戏 tarjan+概率与期望)