bzoj2438: [中山市选2011]杀人游戏

很神奇的一道题啊。。
一开始以为是什么奇奇怪怪的题。。
然后发现是一个强联通+奇奇怪怪的做法
首先,强联通是肯定的。。因为你一个环你只需要问一个点,就可以推出这个环全部的人的状况
然后呢,你就将他缩点
然后呢,我们又知道只有入度为0的块是需要被询问的,因为不为1的块的状态又可以安全地推出来
于是就算一下又多少个入度为0的点

当然这题是不是这样就结束了呢?
当然不是!
还有一种情况,那就是又n个人,你问了n-1个人了,那么最后一个人当然不用问!
那么这是一种什么情况呢
首先,这个块只有一个人
其次,这个块所连出去的边相当于没有连
也就是他连的块还有别的块相连

于是就可以了

#include<cstdio>
#include<cstring>
const int M=300005;
const int N=100005;
int n,m;
struct qq
{
    int x,y,last;
}s[M],s1[M];int num,last[N];
int num1,last1[N];
void init (int x,int y)
{
    num++;
    s[num].x=x;s[num].y=y;
    s[num].last=last[x];
    last[x]=num;
}
int dfn[N],low[N],belong[N],cnt=0,id=0;
int sta[N],ooo=0;
bool in[N];
int lalal[N];//这个块有多少人 
int mymin (int x,int y){return x<y?x:y;}
void dfs (int x)
{
    sta[++ooo]=x;in[x]=true;
    dfn[x]=low[x]=++id;
    for (int u=last[x];u!=-1;u=s[u].last)
    {
        int y=s[u].y;
        if (dfn[y]==-1)
        {
            dfs(y);
            low[x]=mymin(low[x],low[y]);
        }
        else if (in[y]) low[x]=mymin(low[x],dfn[y]);
    }
    if (low[x]==dfn[x])
    {
        cnt++;
        int now;
        do
        {
            now=sta[ooo--];
            belong[now]=cnt;
            in[now]=false;
            lalal[cnt]++;
        }while (now!=x);
    }
}
int du[N];
bool mark[N];
void init2 (int x,int y)
{
    du[y]++;
    num1++;
    s1[num1].x=x;s1[num1].y=y;
    s1[num1].last=last1[x];
    last1[x]=num1;
}
void rebt ()
{
    memset(mark,false,sizeof(mark));
    for (int u=1;u<=n;u++)
    {
        for (int i=last[u];i!=-1;i=s[i].last)
        {
            int y=s[i].y;
            if (belong[u]==belong[y]) continue;
            if (mark[belong[y]]==true) continue;
            mark[belong[y]]=true;
            init2(belong[u],belong[y]);
        }
        for (int i=last[u];i!=-1;i=s[i].last)
            mark[belong[s[i].y]]=false;
    }
    return ;
}
bool Jud (int x)
{
    if (du[x]!=0&&lalal[x]!=1) return false;
    for (int u=last1[x];u!=-1;u=s1[u].last)
    {
        int y=s1[u].y;
        if (du[y]==1) return false;
    }
    return true;
}
int main()
{
    num1=0;memset(last1,-1,sizeof(last1));
    memset(in,false,sizeof(in));
    num=0;memset(last,-1,sizeof(last));
    scanf("%d%d",&n,&m);
    for (int u=1;u<=m;u++)
    {
        int x,y;
        scanf("%d%d",&x,&y);
        init(x,y);
    }
    memset(dfn,-1,sizeof(dfn));
    memset(in,false,sizeof(in));
    for (int u=1;u<=n;u++)
        if (dfn[u]==-1)
            dfs(u);
    rebt();
    //for (int u=1;u<=cnt;u++) printf("%d ",du[u]);
    int ans=0;
    for (int u=1;u<=cnt;u++)
        if (du[u]==0) ans++;
    for (int u=1;u<=cnt;u++)
        if (Jud(u))
            {ans--;break;}
    printf("%.6lf\n",1-(double)ans/(double)n);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值