题目链接 Tree Destructi
题意 给定一棵树,每次可以选定树上的两个叶子,并删去其中的一个。答案每次加上两个选定的叶子之间的距离。
求最后答案的最大值。
首先求出树的某一条直径,令其端点分别为L, R。
把L看成树的根,那么R一定是叶子结点。
对于那些非直径上的点,离他们最远的点肯定是L或R中的一个(可能也有其他的,但是L或R肯定已经最大了)
所以依次把这些非直径上的点删掉,删掉的时候在L和R中选择一个就行了。
最后把直径删掉即可。
时间复杂度$O(nlogn)$ (应该是可以做到$O(n)$的,但是我比较懒)
#include <bits/stdc++.h>
using namespace std;
#define rep(i, a, b) for (int i(a); i <= (b); ++i)
#define dec(i, a, b) for (int i(a); i >= (b); --i)
typedef long long LL;
const int N = 2e5 + 10;
vector <int> v[N];
int n;
int cnt;
int x, y;
int L, R;
int f[N][20];
int a[N];
int deep[N], father[N];
int Father[N];
int vis[N];
int c[N];
int isroot[N];
LL ans = 0;
queue <int> q;
void dfs(int x, int fa, int now){
deep[x] = now;
if (fa){
f[x][0] = fa;
for (int i = 0; f[f[x][i]][i]; ++i)
f[x][i + 1] = f[f[x][i]][i];
}
if (now > cnt) cnt = now, L = x;
for (auto u : v[x]){
if (u == fa) continue;
dfs(u, x, now + 1);
}
}
void dfs2(int x, int fa, int now){
father[x] = fa;
if (now > cnt) cnt = now, R = x;
for (auto u : v[x]){
if (u == fa) continue;
dfs2(u, x, now + 1);
}
}
int LCA(int a, int b){
if (deep[a] < deep[b]) swap(a, b);
for (int i = 0, delta = deep[a] - deep[b]; delta; delta >>= 1, ++i)
if (delta & 1) a = f[a][i];
if (a == b) return a;
dec(i, 19, 0) if (f[a][i] != f[b][i]){
a = f[a][i];
b = f[b][i];
}
return f[a][0];
}
int dis(int x, int y){
int z = LCA(x, y);
return deep[x] + deep[y] - 2 * deep[z];
}
void dfs3(int x, int fa){
vis[x] = 1;
Father[x] = fa;
for (auto u : v[x]){
if (vis[u]) continue;
dfs3(u, x);
++c[x];
}
}
int main(){
scanf("%d", &n);
rep(i, 1, n){
scanf("%d%d", &x, &y);
v[x].push_back(y);
v[y].push_back(x);
}
L = 0, cnt = 0;
dfs(1, 0, 0);
cnt = 0;
R = 0;
memset(father, 0, sizeof father);
dfs2(L, 0, 0);
cnt = 0;
x = R;
while (x){
++cnt;
a[cnt] = x;
x = father[x];
}
memset(vis, 0, sizeof vis);
rep(i, 1, cnt) vis[a[i]] = 1;
rep(i, 1, n) isroot[i] = vis[i];
rep(i, 1, n) if (!vis[i]){
int now = max(dis(i, L), dis(i, R));
ans += 1ll * now;
}
memset(c, 0, sizeof c);
rep(i, 1, cnt) dfs3(a[i], 0);
rep(i, 1, n) if (c[i] == 0 && !isroot[i]) q.push(i);
ans = ans + 1ll * cnt * (cnt - 1) / 2;
printf("%lld\n", ans);
while (!q.empty()){
x = q.front(); q.pop();
vis[x] = 1;
int al = dis(x, L), ar = dis(x, R);
if (al > ar) printf("%d %d %d\n", L, x, x);
else printf("%d %d %d\n", R, x, x);
int u = Father[x];
--c[u];
if (c[u] == 0 && !isroot[u]) q.push(u);
}
rep(i, 1, cnt - 1) printf("%d %d %d\n", a[i], a[cnt], a[i]);
return 0;
}