题意
给定一棵 (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;
}