[中山市选] 杀人游戏 题解
前言
题目链接:洛谷;Hydro & bzoj。
题意简述
警察希望能在
问:根据最优的情况,保证警察自身安全并知道谁是杀手的概率最大是多少?
题目分析
发现,我们如果冒着风险,查验了某一个人,那这个人能够到达的所有点都可以无伤访问。问题就成了在
原图没什么性质,考虑 tarjan 缩成一个 DAG。那么选择了一个点,就可以覆盖 DAG 上,它所在连通块的所有后继。显然,每次选择入度为
设缩点后,入度为
此外,如果有一个点,它没有被访问,但是我们知道其它
时间复杂度线性
代码
// #pragma GCC optimize(3) // #pragma GCC optimize("Ofast", "inline", "-ffast-math") // #pragma GCC target("avx", "sse2", "sse3", "sse4", "mmx") #include <iostream> #include <cstdio> #define debug(a) cerr << "Line: " << __LINE__ << " " << #a << endl #define print(a) cerr << #a << "=" << (a) << endl #define file(a) freopen(#a".in", "r", stdin), freopen(#a".out", "w", stdout) #define main Main(); signed main() { return ios::sync_with_stdio(0), cin.tie(0), Main(); } signed Main using namespace std; int n, m; struct Graph{ struct node{ int to, nxt; } edge[500010 << 1]; int eid, head[500010]; inline void add(int u, int v){ edge[++eid] = {v, head[u]}; head[u] = eid; } inline node & operator [] (const int x){ return edge[x]; } } xym, yzh; int dfn[100010], low[100010], timer; int sccno[100010], scc_cnt; int stack[100010], top; int siz[100010]; bool in_stack[100010]; void tarjan(int now) { dfn[now] = low[now] = ++timer, in_stack[stack[++top] = now] = true; for (int i = xym.head[now]; i; i = xym[i].nxt) { int to = xym[i].to; if (dfn[to] == 0) tarjan(to), low[now] = min(low[now], low[to]); else if (in_stack[to]) low[now] = min(low[now], dfn[to]); } if (low[now] == dfn[now]){ ++scc_cnt; do { int now = stack[top--]; in_stack[now] = false; sccno[now] = scc_cnt; ++siz[scc_cnt]; } while (stack[top + 1] != now); } } int du[100010]; signed main() { scanf("%d%d", &n, &m); for (int i = 1, u, v; i <= m; ++i) { scanf("%d%d", &u, &v); xym.add(u, v); } for (int i = 1; i <= n; ++i) if (!dfn[i]) tarjan(i); for (int i = 1; i <= n; ++i) { static bool vis[100010]; for (int j = xym.head[i], to; to = xym[j].to, j; j = xym[j].nxt) { if (sccno[i] != sccno[to]) { if (vis[sccno[to]]) continue; vis[sccno[to]] = true; ++du[sccno[to]]; yzh.add(sccno[i], sccno[to]); } } for (int j = xym.head[i], to; to = xym[j].to, j; j = xym[j].nxt) vis[sccno[to]] = false; } int ans = 0; for (int i = 1; i <= scc_cnt; ++i) ans += !du[i]; for (int i = 1; i <= scc_cnt; ++i) if (du[i] == 0 && siz[i] == 1) { bool flag = true; for (int j = yzh.head[i], to; to = yzh[j].to, j; j = yzh[j].nxt) { if (du[to] == 1) { flag = false; break; } } if (flag) { --ans; break; } } printf("%.6lf", 1 - 1.0 * ans / n + 1e-10); return 0; }
后记 & 反思
如果是图论,并且给出的图并没有什么性质,考虑缩点。这题那个贪心思想也是后面顺理成章的思路的关键。
本文作者:XuYueming,转载请注明原文链接:https://www.cnblogs.com/XuYueming/p/18300249。
若未作特殊说明,本作品采用 知识共享署名-非商业性使用 4.0 国际许可协议 进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 使用C#创建一个MCP客户端
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现