题解 CF1336A 【Linova and Kingdom】

题意

给定一棵 (n) 个点的有根树(根是 (1)),让你选出 (k)个点打上标记,求出每个点到 (1) 的路径上有多少没被标记的点,让你最大化这个值。

题解

这道题显然可以用贪心来想。

我们考虑一个点 (u) 的贡献。

首先如果我们要选 (u) 那么它的子树内的所有点应该都已经被选,否则选那个没被选的点一定更优。

同理从 (1->u) 的路径上的点一定没有被选。

(dep_u) 代表 (u) 的深度((dep_1=0)),那么如果选 (u) 则值肯定会增加 (dep_u)

那么会减少几个呢?

因为选 (u) 之前 (u) 的后代一定已经被选了,所以选一个 (u) 使得它的后代到 (1) 的路径上没被选的点都减了 (1)(减掉的也就是 (u) 点)。

(sz_u) 代表 (u) 的子树大小,则它的后代共有 (sz_u-1) 个,选 (u) 减少的值也就是 (sz_u-1)

综上选 (u) 的代价是 (dep_u - sz_u + 1),按这个值从小到大排序,取前 (k) 个即可。

显然这样排序一定满足上述性质。

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <queue>
using namespace std;
typedef long long ll;
const int N = 2e5 + 10;
struct node{
	int pre, to;
}edge[N << 1];
int head[N], tot;
int n, k;
int rk[N];
ll dep[N], sz[N], ans;
void add(int u, int v) {
	edge[++tot] = node{head[u], v};
	head[u] = tot;
}
void dfs(int x, int fa) {
	sz[x] = 1;
	for (int i = head[x]; i; i = edge[i].pre) {
		int y = edge[i].to;
		if (y == fa) continue;
		dep[y] = dep[x] + 1;
		dfs(y, x);
		sz[x] += sz[y];
	}
}
bool cmp(int a, int b) {
	return dep[a] - sz[a] + 1 > dep[b] - sz[b] + 1;
}
int main() {
	scanf("%d%d", &n, &k);
	for (int i = 1, a, b; i < n; i++) {
		scanf("%d%d", &a, &b);
		add(a, b);
		add(b, a);
	}
	dfs(1, 0);
	for (int i = 1; i <= n; i++) rk[i] = i;
	sort(rk + 1, rk + n + 1, cmp);
	for (int i = 1; i <= k; i++) {
		int x = rk[i];
		ans += dep[x] - sz[x] + 1;
	}
	printf("%lld", ans);
	return 0;
}
原文地址:https://www.cnblogs.com/zcr-blog/p/14897348.html