Loading

Educational Codeforces Round 35 (Rated for Div. 2) F. Tree Destruction(树的直径/构造)

题目大意是给定一棵树,每次选择两个叶子将其删除,同时将两个叶子之间的简单路径长度加到答案上,找到一种方案使答案最大化,并输出删除的顺序。

首先有一个结论是,距离树上某个节点最远的节点一定是某条直径的某个端点。

证明:反证法。设树上某条直径左端点为L,右端点为R,距离当前节点x最远的点为P。如果x就在直径上,那么P到L的简单路径/P到R的简单的路径长度中较大的那个值一定大于直径的长度,与直径定义矛盾。如果x不在直径上,那么设x到L的简单路径与直径重合的第一个点为Q,此时如果x到P的简单路径与直径不重合,因为x到P更远,所以用PL或者PR替代当前直径一定更优,与直径定义矛盾;如果x到P的简单路径与直径有重合,即x到P必定经过Q,因为xP>max(xL, xR),因此QP > max(QL, QR),同样可知用PL或者PR替代当前直径一定更优,与直径定义矛盾。

所以,最优的方案就是先从远到近删除不在直径上的点,然后删除直径。直径可用两次dfs求出。

#include <bits/stdc++.h>
#define int long long
#define en cout<<endl;
#define de(x) cout<<x;
#define N 200005
using namespace std;
int n, head[N], ver[2 * N], Next[2 * N], tot = 0;
void add(int x, int y) {
	ver[++tot] = y, Next[tot] = head[x], head[x] = tot;
}
int root, root2;
int mxdep = 0;
int dep[3][N];
int fa[N];
struct node {
	int a, b, c;
};
void dfs1(int x, int pre, int d) {
	if(d > mxdep) {
		mxdep = d;
		root = x;
	}
	for(int i = head[x]; i; i = Next[i]) {
		int y = ver[i];
		if(y == pre) continue;
		dfs1(y, x, d + 1);
	}
}
vector<node> v;
void dfs2(int x, int pre, int d, int round) {
	dep[round][x] = d;
	if(round == 1) {
		fa[x] = pre;
	}
	if(d > mxdep) {
		mxdep = d;
		root2 = x;
	}
	for(int i = head[x]; i; i = Next[i]) {
		int y = ver[i];
		if(y == pre) continue;
		dfs2(y, x, d + 1, round);
	}
}
bool link[N];
int ans = 0;
void dfs3(int x, int pre, vector<node> &v) {
	for(int i = head[x]; i; i = Next[i]) {
		int y = ver[i];
		if(y == pre) continue;
		dfs3(y, x, v);
	}
	if(link[x]) return;
	if(dep[1][x] > dep[2][x]) {
		ans += (dep[1][x] - 1);
		v.push_back({x, root, x});
	} else {
		ans += (dep[2][x] - 1);
		v.push_back({x, root2, x});
	}
}
void solve() {
	cin >> n;
	memset(dep, 0, sizeof(dep));
	memset(link, 0, sizeof(link));
	for(int i = 1; i < n; i++) {
		int u, v;
		cin >> u >> v;
		add(u, v);
		add(v, u);
	}
	dfs1(1, 0, 1);
	mxdep = 0;
	dfs2(root, 0, 1, 1);
	dfs2(root2, 0, 1, 2);
	int now = root2;
	vector<int> linkp;
	while(now) {
		link[now] = 1;
		ans += (dep[1][now] - 1);
		if(now != root) linkp.push_back(now);
		now = fa[now];
	}
	vector<node> v;
	dfs3(root, 0, v);
	for(auto x : linkp) {
		v.push_back({x, root, x});
	}
	cout << ans << endl;
	for(auto x : v) {
		cout << x.a << " " << x.b << " " << x.c << endl;
	}
	return;
}
signed main() {
	int T = 1;
	// cin >> T;
	while(T--) {
		solve();
	}
	return 0;
}
posted @ 2022-11-14 17:01  脂环  阅读(37)  评论(0编辑  收藏  举报