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

本文深入探讨了一种基于图论的算法实现,通过连通块、入度等概念确定最少查证人数,并提供了一个概率计算公式。同时,文章还讨论了特殊情况下的算法优化策略。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

这是要被市选艹爆的节奏啊。。。
discuss里面也说了。。 输出的是一个概率,等于1.0-“最少查证的人数”/“总人数”

其实就是缩个点 统计入度为0的连通块 这个就是最少查证人数了
然后discuss有组数据会很良心地提醒你
其实可以省一个点不问
如果有一个大小为1的连通块且它入度为0、无出度或者连到的连通块都还有别人连向它
那它就可以不选 ans–

这里写图片描述
不知道为啥就rank1了 233

#include<bits/stdc++.h>
using namespace std;
const int N=1e5+2;
char B[1<<14],*S=B,*T=B;
#define gc (S==T&&(T=(S=B)+fread(B,1,1<<14,stdin),S==T)?-1:*S++)
inline int read(){
    int x=0,f=1; char ch=gc;
    while(ch<'0' || ch>'9'){if(ch=='-')f=-1; ch=gc;}
    while(ch>='0' && ch<='9'){x=(x<<1)+(x<<3)+ch-'0'; ch=gc;}
    return x*f;
}
struct edge{int x,y,nex;}a[N*3]; int len,fir[N];
void ins(int x,int y){
    a[++len]=(edge){x,y,fir[x]},fir[x]=len;
}
int dfn[N],low[N],id,sz[N],cnt;
bool v[N]; int in[N],st[N],tp,bl[N];
void dfs(int x){
    dfn[x]=low[x]=++id,st[++tp]=x,v[x]=1;
    for(int k=fir[x];k;k=a[k].nex){
        int y=a[k].y;
        if(!dfn[y])dfs(y),low[x]=min(low[x],low[y]);
        else if(v[y])low[x]=min(low[x],dfn[y]);
    }
    if(low[x]==dfn[x]){
        int i; cnt++;
        do{
            i=st[tp--],bl[i]=cnt,sz[cnt]++,v[i]=0;
        }while(i!=x);
    }
}
int main(){
    int n=read(),m=read(),i;
    for(i=1;i<=m;++i){
        int x=read(),y=read(); ins(x,y);
    }
    for(i=1;i<=n;++i)if(!dfn[i])dfs(i);
    for(i=1;i<=m;++i)
        if(bl[a[i].x]!=bl[a[i].y]) in[ bl[a[i].y] ]++;
    int s=0;
    for(i=1;i<=cnt;++i)if(!in[i])++s;
    bool u=0;
    for(i=1;i<=n;++i)
        if(sz[bl[i]]==1 && !in[bl[i]]){
            bool ok=1;
            for(int k=fir[i];k;k=a[k].nex)
                if(in[bl[a[k].y]]==1){ok=0; break;}
            if(ok){u=1; break;}
        }
    s-=u;
    printf("%.6lf\n",1.0-(double)s/n);
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值