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

来源:互联网 发布:淘宝网app 电脑 编辑:程序博客网 时间:2024/05/14 07:31

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

#include<iostream>#include<cstdio>#include<cstring>#define N 100005using 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;}
0 0
原创粉丝点击