P10553 [ICPC2024 Xi'an I] Guess The Tree 题解
挺有意思的题。
思路#
考虑一个比较自然的做法。
我们每次对于一棵树,我们将它的某一条链抽出来。
这样,我们只需要知道这颗树的所有节点与链底的
然后就可以递归处理。
由于交互库不是自适应的。
我们可能可以想到随机一个点。
求出这个点与整颗树的
求出
这样,我们就已经得到了一个比较优秀的做法。
精细实现后可以做到
但是这个还过不了,因为它的操作次数过于随机,经常会变成
我们需要更加好的做法。
考虑什么时候对树的划分最为完全。
一定是一个根到叶子的链可以达到最优的状态。
那么我们不妨将开始随机的点往下跳几步。
具体的:
- 开始随机一个点
。 - 对于现在在的点
,我们遍历到了 。 - 求
。 - 若
,那么让 ,然后继续遍历。 - 否则直接继续遍历。
这样遍历完所有节点后,一定到了一个叶子节点。
但是你写完之后,会发现你的操作次数变成了
思考一下问题在哪里。
我们在往下跳的过程中使用了过多的冗余操作。
我们想要把这些操作也用上。
考虑:
若
为 的子树内一点, 为 的子树外一点,那么 。
所以,我们在不断往下跳的过程中,
这样就可以在恰好
然后你的操作次数就变成了定值,这个定值基于你的实现。
复杂度基于实现。
Code#
这份代码是
/*
! 如果没有天赋,那就一直重复
! Created: 2024/06/05 09:10:14
*/
#include <bits/stdc++.h>
using namespace std;
#define x first
#define y second
// #define int long long
#define mp(x, y) make_pair(x, y)
#define eb(...) emplace_back(__VA_ARGS__)
#define fro(i, x, y) for (int i = (x); i <= (y); i++)
#define pre(i, x, y) for (int i = (x); i >= (y); i--)
inline void JYFILE19();
using i64 = long long;
using PII = pair<int, int>;
bool ST;
const int N = 1100;
const int mod = 998244353;
int n, m, fa[N], sz[N], mp[N][N];
vector<int> to[N];
inline int lca(int x, int y) {
if (mp[x][y]) return mp[x][y];
cout << "? " << x << " " << y << endl;
cin >> mp[x][y];
return mp[y][x] = mp[x][y];
}
inline void upd(int x, int y, int k) {
if (mp[x][y]) return;
mp[x][y] = mp[y][x] = k;
}
inline auto sol(vector<int> all, int las = 0) {
if (all.size() == 0) return 0;
if (all.size() == 1) return all[0];
if (all.size() == 2) return fa[all[0]] = las, all[1];
if (all.size() == 3) {
int x = lca(all[0], all[1]);
for (auto i : all) if (i != x) fa[i] = x;
return x;
}
int x = all[0]; vector<int> res;
for (auto i : all) {
if (lca(i, x) == x) {
for (auto j : all) {
if (i == j) break;
upd(i, j, lca(x, j));
}
x = i;
}
}
for (auto i : all) {
sz[lca(i, x)]++;
res.eb(lca(i, x));
}
sort(res.begin(), res.end(), [&](int x, int y) {
return sz[x] < sz[y];
});
res.erase(unique(res.begin(), res.end()), res.end());
for (int i = 0; i < res.size() - 1; i++)
fa[res[i]] = res[i + 1];
for (auto i : all) {
for (auto j : res) if (i == j) goto ed;
if (lca(i, x) != i) to[lca(i, x)].eb(i);
ed:;
}
for (auto i : res) {
auto nt = to[i];
to[i].clear();
auto x = sol(nt, i);
if (x != i) fa[x] = i;
}
return res.back();
}
signed main() {
JYFILE19();
cin >> n, n = (1 << n) - 1;
vector<int> all;
fill(fa + 1, fa + n + 1, -1);
fro(i, 1, n) all.eb(i), mp[i][i] = i;
sol(all);
cout << "! ";
fro(i, 1, n) cout << fa[i] << " ";
cout << endl;
return 0;
}
bool ED;
inline void JYFILE19() {
// freopen("", "r", stdin);
// freopen("", "w", stdout);
srand(random_device{}());
ios::sync_with_stdio(0), cin.tie(0);
double MIB = fabs((&ED - &ST) / 1048576.), LIM = 32;
// cerr << "MEMORY: " << MIB << endl, assert(MIB <= LIM);
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· 写一个简单的SQL生成工具
· AI 智能体引爆开源社区「GitHub 热点速览」
· C#/.NET/.NET Core技术前沿周刊 | 第 29 期(2025年3.1-3.9)