Codeforces 911F Tree Destruction(贪心 && 树的直径)

题目链接  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;
}

原文地址:https://www.cnblogs.com/cxhscst2/p/8277345.html

时间: 2024-07-31 16:57:50

Codeforces 911F Tree Destruction(贪心 && 树的直径)的相关文章

CF.911F.Tree Destruction(构造 贪心)

题目链接 \(Description\) 一棵n个点的树,每次可以选择树上两个叶子节点并删去一个,得到的价值为两点间的距离 删n-1次,问如何能使最后得到的价值最大,并输出方案 \(Solution\) 树上距离,求最大,可以考虑下树的直径 假如已知树的直径u->v,那么任意一点x到达其他点的最远距离就是u,v中一点(如果不是这样,那直径一定可以更长而不是uv) 假设x距u最远,那肯定是删x 删直径上的点(直径端点)会导致一些点取不到最远距离 既然这样按顺序删非直径上的点,最后删直径端点 #in

ZOJ 3820 Building Fire Stations 贪心+树的直径

不知道为什么是对的,但是举不出反例,比赛的时候队友提出找树的直径,不过我没敢写,真是可惜. 具体做法就是先找到原树的直径,然后删去最中间的一条边,变为两个子树,分别球两颗子树的直径中点,便是答案了. #include <cstdio> #include <cstring> #include <algorithm> #include <queue> #include <stack> #include <map> #include <

Farthest Nodes in a Tree (求树的直径)

题目链接,密码:hpu Description Given a tree (a connected graph with no cycles), you have to find the farthest nodes in the tree. The edges of the tree are weighted and undirected. That means you have to find two nodes in the tree whose distance is maximum a

CF911F Tree Destruction

题意翻译 给你一棵树,每次挑选这棵树的两个叶子,加上他们之间的边数(距离),然后将其中一个点去掉,问你边数(距离)之和最大可以是多少. 题目描述 You are given an unweighted tree with n n n vertices. Then n−1 n-1 n−1 following operations are applied to the tree. A single operation consists of the following steps: choose t

CodeForces 379F 树的直径 New Year Tree

题意:每次操作新加两个叶子节点,每次操作完以后询问树的直径. 维护树的直径的两个端点U,V,每次计算一下新加进来的叶子节点到U,V两点的距离,如果有更长的就更新. 因为根据树的直径的求法,若出现新的直径,一定是到U或者到V距离最远. 1 #include <iostream> 2 #include <cstdio> 3 #include <cstring> 4 #include <algorithm> 5 6 const int maxn = 1000000

codeforces GYM 100114 J. Computer Network tarjan 树的直径 缩点

J. Computer Network Time Limit: 1 Sec Memory Limit: 256 MB 题目连接 http://codeforces.com/gym/100114 Description The computer network of “Plunder & Flee Inc.” consists of n servers and m two-way communication links. Two servers can communicate either thr

[Codeforces 1295E]Permutation Separation(线段树+贪心)

[Codeforces 1295E]Permutation Separation(线段树+贪心) 题面 给出一个排列\(p_1,p_2,...p_n\).初始时你需要选择一个位置把排列分成左右两个.然后在两个序列间移动元素使得左边序列的所有元素都比右边的所有元素小.给出每个元素\(p_i\)从一个序列移动到另一个序列的代价\(a_i\). 分析 显然最后得到的序列是小的数在一边,大的数在另一边.设从值为\(i\)的元素处分开之后移动代价为\(ans_i\). 一开始假设所有数都移到右边序列,那么

codeforces GYM 100114 J. Computer Network 无相图缩点+树的直径

题目链接: http://codeforces.com/gym/100114 Description The computer network of “Plunder & Flee Inc.” consists of n servers and m two-way communication links. Two servers can communicate either through a direct link, or through a chain of links, by relayi

codeforces 455C C. Civilization(树形dp+树的直径+并查集)

题目链接: codeforces 455C 题目大意: 给出一些点,他们之间初始存在一些边,给出两种操作,第一种是查询某个点所在的树的直径,另一种是将两个树合并,要求使合并后的树的直径最小. 题目分析: 首先算取没做操作前的连通块里的树的直径,也就是先dfs一遍,找到深度最大的点,然后从这个点再搜,找到的最远的距离就是这棵树的直径,因为可以证明从根搜深度最大的点一定是树的直径的一个端点,因为它可以通过到达次大的深度的点或者找到与它公共祖先不在根处的获得树的直径. 然后每次合并,我们可以知道得到的