题解:P16445 [XJTUPC 2026] The Whole Rest
树上启发式合并做法
思路
首先注意到路径最小代价显然为
选顶点
考虑从子节点向父节点合并的过程,使用 map 记录每个节点子树中相同
构造路径的部分使用深搜即可,我的做法是先走非两点路径上的点,再走路径上的点,总时间复杂度
::::info[代码]
#include <bits/stdc++.h>
#define endl '\n'
using namespace std;
using i64 = long long;
int main() {
ios::sync_with_stdio(false);
cin.tie(0);
int n;
cin >> n;
vector<vector<pair<int, int>>> e(n + 1);
for (int i = 1; i < n; i++) {
int u, v, w;
cin >> u >> v >> w;
e[u].emplace_back(v, w);
e[v].emplace_back(u, w);
}
vector<map<int, int>> mp(n + 1);
vector<int> dep(n + 1, 0);
int u = 1, v = 1, mx = 0;
auto merge = [&](int x, int y) {
if (mp[x].size() > mp[y].size()) {
mp[y].swap(mp[x]);
}
for (auto [val, id] : mp[x]) {
if (mp[y].contains(val)) {
int U = mp[y][val], V = id;
int now = dep[U] + dep[V] - 2 * dep[y]; // y 此时肯定是 LCA
if (now > mx) {
mx = now;
u = U, v = V;
}
if (dep[U] < dep[id]) {
mp[y][val] = id;
}
} else {
mp[y][val] = id;
}
}
mp[x].clear();
};
auto dfs = [&](auto &&self, int u, int f, int val) -> void {
dep[u] = dep[f] + 1;
mp[u][val] = u;
for (auto &[v, w] : e[u]) {
if (v == f) {
continue;
}
self(self, v, u, val ^ w);
merge(v, u);
}
};
dfs(dfs, 1, 0, 0);
vector<bool> onPath(n + 1, false);
auto dfs2 = [&](auto &&self, int u, int f) -> void {
if (u == v) {
onPath[u] = true;
return;
}
for (auto &[vv, w] : e[u]) {
if (vv == f) {
continue;
}
self(self, vv, u);
if (onPath[vv]) {
onPath[u] = true;
return;
}
}
};
dfs2(dfs2, u, 0);
vector<int> path;
auto dfs3 = [&](auto &&self, int u, int f) -> void {
path.push_back(u);
for (auto [v, w] : e[u]) {
if (v == f || onPath[v]) {
continue;
}
self(self, v, u);
path.push_back(u);
}
for (auto [v, w] : e[u]) {
if (onPath[v] && v != f) {
self(self, v, u);
break;
}
}
if (u == v) {
return;
}
};
dfs3(dfs3, u, 0);
cout << path.size() << endl;
for (auto i : path) {
cout << i << " ";
}
return 0;
}
::::