题目大意:给你一棵树,每次挑选这棵树的两个叶子,把答案加上他们之间的距离,然后将其中一个点去掉,问你距离之和最大可以是多少。要求输出方案
题解:求出直径和直径上的点,因为树上一个点可以提供的最大贡献为它到直径中的一个端点的距离,于是把不在直径上的点先删去(注意要按深度一个个删,因为要求删叶子)。
卡点:1.没有按深度从大到小删,删除了非叶子节点
2.爆$long\;long$
C++ Code:
#include <cstdio>
#include <cstring>
#define maxn 200010
#define int long long
using namespace std;
int head[maxn], cnt;
struct Edge {
int to, nxt;
} e[maxn << 1];
void add(int a, int b) {
e[++cnt] = (Edge) {b, head[a]}; head[a] = cnt;
}
int n, x, y;
int depx[maxn], depy[maxn], fa[maxn];
int q[maxn], h, t;
int ans, v[maxn], tot;
bool isd[maxn];
inline int max(int a, int b) {return a > b ? a : b;}
int bfs(int rt, int *dep) {
q[h = t = 0] = rt;
while (h <= t) {
int u = q[h++];
for (int i = head[u]; i; i = e[i].nxt) {
int v = e[i].to;
if (!dep[v]) {
fa[v] = u;
dep[v] = dep[u] + 1;
q[++t] = v;
}
}
}
return q[t];
}
void up(int rt) {
isd[rt] = true;
v[tot++] = rt;
while (rt != y) {
rt = fa[rt];
v[tot++] = rt;
isd[rt] = true;
}
}
signed main() {
scanf("%I64d", &n);
for (int i = 1; i < n; i++) {
int a, b;
scanf("%I64d%I64d", &a, &b);
add(a, b);
add(b, a);
}
if (n == 1) {puts("0"); return 0;}
if (n == 2) {puts("1"); puts("1 2 1"); return 0;}
depx[1] = 1; x = bfs(1, depx);
memset(depx, 0, sizeof depx);
depx[x] = 1; y = bfs(x, depx);
depy[y] = 1; bfs(y, depy);
up(x);
for (int i = 1; i <= n; i++) {
if (!isd[i]) ans += max(depx[i], depy[i]) - 1;
else ans += depy[i] - 1;
}
printf("%I64d\n", ans);
for (int i = t; ~i; i--) {
if (!isd[q[i]]){
if (depx[q[i]] > depy[q[i]]) printf("%I64d %I64d %I64d\n", x, q[i], q[i]);
else printf("%I64d %I64d %d\n", y, q[i], q[i]);
}
}
for (int i = 0; i < tot - 1; i++) printf("%I64d %I64d %I64d\n", y, v[i], v[i]);
return 0;
}