当前位置: 首页 > news >正文

P11988 [JOIST 2025] 宇宙怪盗 题解

Description

这是一道交互题。本题中,交互库可能是自适应的。

有一张 \(N\) 个点 \(M\) 条边的无向连通图。点编号 \(0\sim N-1\),边编号 \(0\sim M-1\),第 \(i\)\(0 \leq i \leq M-1\))条边双向连接点 \(U_i\)\(V_i\)

有一把钥匙藏在某一个点上,而有一个宝箱藏在另一个节点上。你需要通过至多 \(300\) 次询问确定钥匙所在的节点编号和宝箱所在的节点编号:

询问

对于 \(i=0,1,\ldots,M-1\),将第 \(i\) 条边设置为单向通行。

  • 具体地,构造长度为 \(M\)\(01\) 序列 \(x_0\sim x_{M-1}\)\(x_i=0\) 表示第 \(i\) 条边从 \(U_i\) 指向 \(V_i\)\(x_i=1\) 表示第 \(i\) 条边从 \(V_i\) 指向 \(U_i\)

交互库会返回,在这张图中,是否能从钥匙所在的节点到达宝箱所在的节点。

你需要用至多 \(70\) 次询问确定钥匙所在的节点 \(A\) 和宝箱所在的节点 \(B\)

\(N\leq 10000,M\leq 15000\)

Solution

先考虑树怎么做。注意到我们如果能够找到任何一个合法的定向方案使得 \(A\) 能到 \(B\),那么把这棵树的拓扑序拿出来就能二分了。因为 \(A\) 此时拓扑序一定在 \(B\) 之前,二分找到最长的前缀 \([1,d]\),让拓扑序数组上 \([1,d]\)\([d+1,n]\) 之间的边反向,如果 \(A\) 还能到 \(B\) 就说明 \(A\)\([d+1,n]\) 里,否则 \(A\)\([1,d]\) 里。对 \(B\) 是同理的。

现在再来考虑怎么找到一组这样的定向方案。

对于菊花的情况,由于 \(A\)\(B\) 至少有一个二进制位不同,所以选择一个二进制位,让这一位为 \(0\) 的边方向指向根,为 \(1\) 的方向指向叶子。再询问将这个图反向的结果,通过 \(2\log N\) 次询问一定能够得到满足条件的方案。

对于普通树,考虑通过点分治将问题变得类似菊花,即钦定 \(A\)\(B\) 的路径经过当前的根 \(x\),我们让 \(x\) 每个儿子子树内的边方向相同,就变成菊花了,然后让点分树同层的 \(x\) 并行做,总次数是 \(O(\log^2N)\)

注意到 \(x\) 是重心,所以 \(x\) 儿子子树最大的大小不超过 \(\frac{sz_x}{2}\),容易发现一定存在 \(x\) 儿子的一个划分方案使得每个集合内的子树大小和在 \(\left[\frac{sz_x}{3},\frac{2sz_x}{3}\right]\) 内,随便找到一个这样的划分,将这两个划分看成两个子树做上面的做法,再对这两个划分分治即可。

总次数为 \(2\log_{\frac{3}{2}}N+2\log_2N\)

Code

#include "thief.h"
#include <bits/stdc++.h>#ifdef ORZXKR
#include "grader.cpp"
#endifconst int kMaxN = 6e4 + 5, kMaxM = 2e4 + 5;int n, m, rt, mxd, cnt;
int u[kMaxM], v[kMaxM], sz[kMaxN], mx[kMaxN], dep[kMaxN], real[kMaxN], now[kMaxN];
bool ont[kMaxM], del[kMaxN];
std::vector<std::pair<int, int>> G[kMaxN], T[kMaxN];
std::vector<int> eid[kMaxN];void add(int u, int v, int id) {T[u].emplace_back(v, id), T[v].emplace_back(u, id);
}bool ask(std::vector<int> perm) {static int pos[kMaxN];std::vector<int> vec(m);for (int i = 1; i <= n; ++i) pos[perm[i - 1]] = i;for (int i = 1; i <= m; ++i) vec[i - 1] = (pos[u[i]] > pos[v[i]]);return query(vec);
}bool ask(bool *dir, bool op = 0) {std::vector<int> vec(m);for (int i = 1; i <= m; ++i) vec[i - 1] = dir[i] ^ op;return query(vec);
}struct Node {int deg[kMaxN] = {0}, _deg[kMaxN] = {0};std::vector<int> G[kMaxN];void add(int u, int v) { G[u].emplace_back(v), ++_deg[v]; }std::vector<int> getperm() {std::queue<int> q;std::vector<int> perm;for (int i = 1; i <= n; ++i) {deg[i] = _deg[i];if (!deg[i])q.emplace(i);}for (; !q.empty();) {int u = q.front(); q.pop();perm.emplace_back(u);for (auto v : G[u]) {if (!--deg[v]) q.emplace(v);}}return perm;}bool query() { return ask(getperm()); }
} gr[100];void build() {static bool vis[kMaxN];std::fill_n(vis + 1, n, 0);std::function<void(int)> dfs = [&] (int u) {vis[u] = 1;for (auto [v, id] : G[u]) {if (!vis[v]) {ont[id] = 1, T[u].emplace_back(v, id), T[v].emplace_back(u, id);dfs(v);}}};dfs(1);
}void getsz(int u, int fa) {sz[u] = 1, mx[u] = 0;for (auto [v, id] : T[u]) {if (v == fa || del[v]) continue;getsz(v, u);sz[u] += sz[v], mx[u] = std::max(mx[u], sz[v]);}
}void getrt(int u, int fa, int tot) {mx[u] = std::max(mx[u], tot - sz[u]);if (mx[u] < mx[rt]) rt = u;for (auto [v, id] : T[u]) {if (v == fa || del[v]) continue;getrt(v, u, tot);}
}void dfs(int u, int fa, int rt) {dep[u] = dep[fa] + 1;now[real[u]] = u;for (auto [v, id] : T[u]) {if (v == fa || del[v]) continue;if (id) eid[rt].emplace_back(id);dfs(v, u, rt);}
}void solve(int u, int d) {static int pid[kMaxN];mx[rt = 0] = 1e9, getsz(u, 0), getrt(u, 0, sz[u]);u = rt, dep[u] = 1, now[real[u]] = u;std::vector<int> vid;int tot = 0;for (auto [v, id] : T[u]) {if (del[v]) continue;eid[v] = (id ? std::vector<int>{id} : std::vector<int>{});pid[v] = id;vid.emplace_back(v), dfs(v, u, v);tot += eid[v].size();}std::sort(vid.begin(), vid.end(), [&] (int i, int j) { return eid[i].size() < eid[j].size(); });if (!tot) return;int id1 = 0, id2 = 0;std::vector<int> e1, e2;for (int i = 0, now = 0; i < vid.size(); ++i) {now += eid[vid[i]].size();if (now >= tot / 3) {real[id1 = ++cnt] = real[u];if (i + 1 < vid.size()) real[id2 = ++cnt] = real[u];for (int j = 0; j <= i; ++j) {int v = vid[j];for (auto id : eid[v]) e1.emplace_back(id);add(id1, vid[j], pid[vid[j]]);}for (int j = i + 1; j < vid.size(); ++j) {int v = vid[j];for (auto id : eid[v]) e2.emplace_back(id);add(id2, vid[j], pid[vid[j]]);}// std::cerr << "fuckkk " << u << ' ' << tot << ' ' << now << ' ' << tot - now << '\n';break;}}for (auto id : e1) {int x = ::u[id], y = ::v[id];if (dep[now[x]] > dep[now[y]]) std::swap(x, y);gr[2 * d - 1].add(x, y), gr[2 * d].add(y, x);}for (auto id : e2) {int x = ::u[id], y = ::v[id];if (dep[now[x]] > dep[now[y]]) std::swap(x, y);gr[2 * d - 1].add(y, x), gr[2 * d].add(x, y);}mxd = std::max(mxd, 2 * d);del[u] = 1;if (tot == 1) return;assert(id1 && id2);// std::cerr << "shabi " << u << ' ' << cnt << ' ' << d << ' ' << sz[u] << ' ' << id1 << ' ' << id2 << '\n';// std::cerr << e1.size() << ' ' << e2.size() << '\n';if (id1) solve(id1, d + 1);if (id2) solve(id2, d + 1);
}void solve(int N, int M, std::vector<int> U, std::vector<int> V) {n = N, m = M;for (int i = 1; i <= m; ++i) {u[i] = U[i - 1] + 1, v[i] = V[i - 1] + 1;G[u[i]].emplace_back(v[i], i);G[v[i]].emplace_back(u[i], i);}build();cnt = n;for (int i = 1; i <= n; ++i) real[i] = i;solve(1, 1);for (int d = 1; d <= mxd; ++d) {// std::cerr << gr[d].query() << '\n';if (gr[d].query()) {auto perm = gr[d].getperm();static int pos[kMaxN];for (int i = 1; i <= n; ++i) pos[perm[i - 1]] = i;int a = 0, b = 0;{int L = 1, R = n + 1, res = 1;while (L + 1 < R) {int mid = (L + R) >> 1;std::vector<int> vec(m);for (int i = 1; i <= m; ++i) {int x = pos[u[i]], y = pos[v[i]];if (x < mid || y < mid) vec[i - 1] = (x < y);else vec[i - 1] = (x > y);}if (query(vec)) L = res = mid;else R = mid;}a = perm[res - 1];}{int L = 0, R = n, res = n;while (L + 1 < R) {int mid = (L + R) >> 1;std::vector<int> vec(m);for (int i = 1; i <= m; ++i) {int x = pos[u[i]], y = pos[v[i]];if (x > mid || y > mid) vec[i - 1] = (x < y);else vec[i - 1] = (x > y);}if (query(vec)) R = res = mid;else L = mid;}b = perm[res - 1];}answer(a - 1, b - 1);return;}// std::cerr << gr[i].ask() << '\n';}
}
http://www.hskmm.com/?act=detail&tid=27328

相关文章:

  • 2025 年石墨烯厂家最新推荐榜单:氧化 / 羧基化 / 巯基化 / 羟基化 / 氨基化 / 氮掺杂石墨烯优质厂商全面解析与选购指南
  • 2025铝合金牺牲阳极厂家推荐榜:牺牲阳极阴极保护工业防腐技术
  • 2025 年压滤机厂家最新推荐排行榜:隔膜 / 污泥 / 真空 / 板框 / 带式压滤机厂家权威甄选指南板框/带式/污泥脱水/气化渣脱水专用/污泥专用脱水压滤机厂家推荐
  • 2025 年最新推荐!点胶机源头厂家权威排行榜:涵盖自动 / 果冻胶 / 无痕内衣 / 烫钻等多类型设备,助企业精准选品
  • 2025 年制袋机厂家推荐,广州速益科技提供多品类自动化设备与专业售后服务
  • 2025 年最新推荐云手机服务平台权威榜单:商用办公 / 多开设备 / 托管定制 / 租赁等场景优质品牌全解析
  • Octane 2022 汉化版适配C4D 2021-2023实用指南
  • 通俗易懂:什么是PostgreSQL中级认证(PGCP认证)
  • SQL Server 限制IP访问数据库的设置方法及注意事项
  • 2025 升降桌源头厂家最新推荐榜:聚焦国产新锐与实力大厂,解锁高性价比选购指南升降桌框/升降办公桌/升降办公桌框厂家推荐
  • 产品经理必看:原型设计工具三大能力解析(交互/AI/素材库)
  • AI 智能体 RAG 入门教程
  • 基于 RS 锁存器的真随机数生成器
  • 实用指南:会议安排问题之贪心算法
  • 10 9
  • 2025 年高压反应釜厂家最新推荐排行榜:涵盖多材质多类型设备,精选实力厂家助企业精准选购高温/加氢/不锈钢/实验室/钛材/镍材高压反应釜厂家推荐
  • CF1083D The Fair Nuts getting crazy(线段树/单调栈)
  • 2025 年最新水环真空泵生产厂家排行榜:优质品牌最新推荐,助企业精准选购适配设备罗茨水环真空泵组/山东水环真空泵/水环真空泵负压站/水环真空泵机组厂家推荐
  • QLabel加入点击的几种方式
  • 2025 年看守所会见律师联系方式推荐,徐义明律师专业刑事辩护与高效会见服务
  • 软件技术基础第一次作业1
  • 昇腾个人学习笔记
  • iOS 26 性能测试全攻略,从界面体验到性能指标
  • 市场宽度实时定时版
  • 2025 年光伏展会预定,上海伏勒密科技有限公司打造覆盖全产业链的国际化新能源会展服务平台
  • ant-design-vue 4.x版本在谷歌浏览器80版本中样式不显示的问题
  • 实验结论
  • 2025 年喷雾干燥机厂家最新推荐:国内实力企业排行榜,含离心式 / 压力式 / 实验型设备品牌深度解析
  • oracle修改监听端口
  • 2025 年干燥机厂家最新推荐排行榜:聚焦国内优质干燥机厂家,精选实力品牌助力企业精准选购