luogu P4819 [中山市选] 杀人游戏 题解 【强连通分量+缩点】
题目链接
思路分析
首先考虑这道题的连通性。容易发现这种类型的题目会容易产生环形的状态转移。假设我们知道了其中的一个点是否是黑白点,那么我们就可以知道所有点是否是黑白点。容易陷入一个误区:我们只能通过一个点知道他所相邻的最直接的点,如何确定相邻的点的状态?注意本题求的是存活且找到黑点的概率而不是期望一类,我们只需要能在黑点前停下就可以了。注意 tarjan
强连通分量的一个重要性质:避免重边!虽然我之前做的题没有刻意卡这个性质。
代码
#include<iostream>
#include<cstdio>
#include<vector>
#include<iomanip>
using namespace std ;
const int N = 1e5+10 ;
const int M = 3e5+10 ;
int n ,m ;
vector<int> edge[N] ;
int low[N] ,dfn[N] ,tim ,sta[N] ,top ,col[N] ,cnt ;
bool vis[N] ;
inline void tarjan(int u){
sta[++top] = u ;
vis[u] = 1 ;
low[u] = dfn[u] = ++tim ;
for(int v : edge[u]){
if(!dfn[v]){
tarjan(v) ;
low[u] = min(low[u] , low[v]) ;
}else if(vis[v]) low[u] = min(low[u] , dfn[v]) ;
}
if(low[u] == dfn[u]){
col[u] = ++cnt ;
while(sta[top] != u) col[sta[top]] = cnt ,vis[sta[top--]] = 0 ;
vis[u] = 0 ,top-- ;
}
}
int in[N] ,siz[N] ,tot ;
vector<int> g[N] ;
bool vise[M] ;
int main(){
ios::sync_with_stdio(0) ,cin.tie(0) ,cout.tie(0) ;
cin >> n >> m ;
for(int i = 1,u,v;i <= m;++i){cin >> u >> v ;edge[u].push_back(v) ;}
for(int i = 1;i <= n;++i) if(!dfn[i]) tarjan(i) ;
for(int i = 1;i <= n;++i) siz[col[i]]++ ;
for(int i = 1;i <= n;++i){
for(int j : edge[i]){
if(col[i] == col[j] || vise[col[j]]) continue ;
g[col[i]].push_back(col[j]) ;
vise[col[j]] = 1 ;in[col[j]]++ ;
}
for(int j : edge[i]) vise[col[j]] = 0 ;
}
bool flag = 1 ;
for(int i = 1;i <= cnt;++i){
if(!in[i]) tot++ ;
if(in[i] || siz[i] > 1) continue ;
if(flag == 0) continue ;
bool pp = 1 ;
for(int j : g[i])
if(in[j] <= 1){
pp = 0 ;
break ;
}
if(pp) tot-- ,flag = 0 ;
}
cout << fixed << setprecision(6) << 1.0 * (n - tot) / n ;
return 0 ;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具