题意
有两颗树,在每棵树中选择一个结点并将它们两相连使得对于新的树的任意两点的距离总和最小。
思路
设 \(sz[u]\) 表示子树 \(u\) 的大小。
显然,将两数重心连接结果最优秀(根据树的重心定义)。
对于每条边,取它两端 \(sz\) 较小的那一个点 \(u\),那么这条边贡献为 \(sz[u] * (n - sz[u])\),\(n\) 为总结点数。
最后统计那一条连接的边,它的贡献就是两棵树的大小的乘积。
代码
// Problem: Cotree
// Contest: Virtual Judge - HDU
// URL: https://vjudge.net.cn/problem/HDU-6567
// Memory Limit: 256 MB
// Time Limit: 1000 ms
//
// Powered by CP Editor (https://cpeditor.org)
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 100010;
struct edge {
int to, next;
} e[N * 2];
int head[N], idx = 1;
void add(int u, int v) {
idx++, e[idx].to = v, e[idx].next = head[u], head[u] = idx;
}
int tot;
bool vis[N];
void dfs(int u, int fa) {
tot++; vis[u] = 1;
for (int i = head[u]; i; i = e[i].next) {
int to = e[i].to;
if (to == fa) continue;
dfs(to, u);
}
}
int sz[N];
int mx[N];
int root;
int rt1, rt2;
void getroot(int u, int fa) {
sz[u] = 1;
mx[u] = 0;
for (int i = head[u]; i; i = e[i].next) {
int to = e[i].to;
if (to == fa) continue;
getroot(to, u);
sz[u] += sz[to];
mx[u] = max(mx[u], sz[to]);
}
mx[u] = max(mx[u], tot - sz[u]);
if (mx[u] * 2 <=
tot) {
root = u;
}
}
void dfs2(int u, int fa) {
sz[u] = 1;
for (int i = head[u]; i; i = e[i].next) {
int to = e[i].to;
if (to == fa) continue;
dfs2(to, u);
sz[u] += sz[to];
}
}
int n;
signed main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
cin >> n;
for (int i =1; i <= n - 2; i++) {
int u , v;
cin >> u >> v;
add(u, v), add(v, u);
}
for (int i = 1; i <= n; i++) {
if (!vis[i]) {
tot = 0;
dfs(i, 0);
getroot(i, 0);
if (!rt1) rt1 = root;
else rt2 = root;
}
}
dfs2(rt1, rt2);
dfs2(rt2, rt1);
int ans = 0;
for (int i = 2; i <= idx; i += 2) {
int u = e[i].to, v = e[i ^ 1].to;
ans += min(sz[u], sz[v]) * (n - min(sz[u], sz[v]));
}
ans += sz[rt1] * sz[rt2];
cout << ans << endl;
return 0;
}
标签:sz,head,idx,HDU6567,Cotree,next,int,mx
From: https://www.cnblogs.com/Yuan-Jiawei/p/18348489