P12447 [COTS 2025] 砍树 / Stablo
EuphoricStar · · 题解
有点意思的题。
考虑逐个加点。假设已经维护出来原树的一个连通块,我们现在要加入一个新点,即挂一个叶子到这个连通块上。
考虑边分治去找新点应该挂到哪个点上。每次找到一条边
问题还剩下如何保证新点一定是连通块的叶子。考虑将所有点按照与
所以我们就以
#include <bits/stdc++.h>
#define pb emplace_back
#define fst first
#define scd second
#define mkp make_pair
#define mems(a, x) memset((a), (x), sizeof(a))
using namespace std;
typedef long long ll;
typedef double db;
typedef unsigned long long ull;
typedef long double ldb;
typedef pair<int, int> pii;
const int maxn = 520;
int n, p[maxn];
bool vis[maxn];
pii e[maxn];
vector<int> G[maxn];
map< pair< pair<int, int>, int >, int > mp;
inline int ask(int a, int b, int c) {
if (mp.find(mkp(mkp(a, b), c)) != mp.end()) {
return mp[mkp(mkp(a, b), c)];
}
printf("? %d %d %d\n", a, b, c);
fflush(stdout);
int x;
scanf("%d", &x);
return mp[mkp(mkp(a, b), c)] = x;
}
int sz[maxn], fa[maxn];
void dfs(int u, int t) {
sz[u] = 1;
fa[u] = t;
for (int v : G[u]) {
if (v == t || !vis[v]) {
continue;
}
dfs(v, u);
sz[u] += sz[v];
}
}
void dfs2(int u, int fa) {
vis[u] = 0;
for (int v : G[u]) {
if (v == fa || !vis[v]) {
continue;
}
dfs2(v, u);
}
}
void solve() {
scanf("%d", &n);
for (int i = 1; i <= n; ++i) {
p[i] = i;
}
stable_sort(p + 2, p + n + 1, [&](const int &x, const int &y) {
return ask(1, x, y) == 1;
});
G[1].pb(p[2]);
G[p[2]].pb(1);
e[1] = mkp(1, p[2]);
for (int i = 3; i <= n; ++i) {
mems(vis, 0);
for (int j = 1; j < i; ++j) {
vis[p[j]] = 1;
}
while (1) {
int cnt = 0, u = 0;
for (int j = 1; j < i; ++j) {
if (vis[p[j]]) {
++cnt;
u = p[j];
}
}
if (cnt == 1) {
e[i - 1] = mkp(u, p[i]);
G[u].pb(p[i]);
G[p[i]].pb(u);
break;
}
dfs(u, -1);
int mn = 1e9, w = -1;
for (int j = 1; j < i; ++j) {
int v = p[j];
if (u != v && vis[v] && max(sz[v], cnt - sz[v]) < mn) {
mn = max(sz[v], cnt - sz[v]);
w = v;
}
}
if (ask(p[i], w, fa[w]) == 1) {
dfs2(fa[w], w);
} else {
dfs2(w, fa[w]);
}
}
}
puts("!");
for (int i = 1; i < n; ++i) {
printf("%d %d\n", e[i].fst, e[i].scd);
}
}
int main() {
int T = 1;
// scanf("%d", &T);
while (T--) {
solve();
}
return 0;
}