题解:AT_agc018_d [AGC018D] Tree and Hamilton Path
posted on 2025-02-07 00:35:13 | under | source
等价于找一个排列 \(p\),求最小权值 \(\sum\limits_{i<n} dist(p_i,p_{i+1})\)。
乍一看难以下手,任意定一个根,转化为 \(\sum\limits_{i<n} dep_{p_i}+dep_{p_{i+1}}-2dep_{lca(p_i,p_{i+1})}\)。
前两项只和起点终点有关,即为 \(2\sum dep_i-dep_{p_1}-dep_{p_n}\)。
对于 \(lca\) 项,一个简单的想法是尽量取到根。于是很容易想到定重心为根。同时选取两个最小的 \(dep\) 分别做起点终点,这是上界。
显然起点为重心,终点为它的一个儿子。我们发现,当只有一个重心时,确实可以构造达到上界。具体来说,将每个儿子子树染色,删去终点,将它们排个序,然后经典地让 \(i\) 和 \(i+\frac n2\) 配对,得到若干异色配对(可能剩下一个电灯泡),因为不存在绝对众数所以可行。然后拼起来即可。
对于有两个重心的情况,不妨在两个重心之间建立虚点,然后同上。结论就是删去重心之间的边。
\(O(n)\)。
代码
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define pir pair<int, int>
const int N = 1e5 + 5;
int n, u, v, w, siz[N], d[N], ans;
vector<pir> to[N];
vector<int> zx;
inline void add(int u, int v, int w) {to[u].push_back({v, w}), to[v].push_back({u, w});}
inline void dfs(int u, int from){
siz[u] = 1;
int mx = 0;
for(auto _ : to[u]){
int v = _.first, w = _.second;
if(v ^ from) dfs(v, u), siz[u] += siz[v], mx = max(mx, siz[v]);
}
mx = max(mx, n - siz[u]);
if(mx <= n / 2) zx.push_back(u);
}
inline void dfs2(int u, int from){
for(auto _ : to[u]){
int v = _.first, w = _.second;
if(v ^ from) d[v] = d[u] + w, dfs2(v, u);
}
ans += d[u] * 2;
}
signed main(){
cin >> n;
for(int i = 1; i < n; ++i) scanf("%lld%lld%lld", &u, &v, &w), add(u, v, w);
dfs(1, 0);
if(zx.size() == 1){
int rot = zx[0], mi = 1145141919;
dfs2(rot, 0);
for(auto _ : to[rot]) mi = min(mi, _.second);
cout << ans - mi;
}
else{
int x = zx[0], y = zx[1], xzy;
for(auto _ : to[x]) if(_.first == y) xzy = _.second;
dfs2(x, 0);
cout << ans - xzy;
}
return 0;
}

浙公网安备 33010602011771号