题目链接: Linova and Kingdom
大致题意
有n座城市, n-1条连通城市间的双向道路. 城市1是首都, 所有的城市可以看做是以1号点为根的树形结构.
我们需要从中选择m座城市, 让其发展工业, 其余的城市则发展旅游业.
从工业城市i前往城市1的最短路径所经过的旅游业城市数目, 定义为i城市的贡献度.
你可以自定义m座发展工业的城市, 问: m座城市的最大贡献度之和是多少.
解题思路
首先不难贪心联想到: 我们选择叶子结点成为工业城市好像要更优一些.
假设1~n所有城市形成一条链, 此时如果m=1必然我们选择叶子结点最优. 那如果m=2呢? 我们应选择最靠下的两个节点.
考虑回正常的树. 我们之所以认为选择叶子节点会更优, 是因为对于树中任意一条链而言, 这条链上距离根节点最远的点为叶子节点. 如果m=2, 此时树有两条链构成, 一条链上只有一个点(距离根节点的距离是1), 另外一条链很长. 我们不难发现, 我们应该在长链上选择链尾的两个节点.
因此我们得出结论: 我们并不是选择叶子节点更优, 而是选择深度更大的节点更优.
接下来我们再思考, 假设有两个相同深度节点a, b. 节点a有2个为叶子节点的子节点, 节点b有1个为叶子节点的子节点, 此时如果我在a, b中做选择, 我应该选择节点a.
当选择了某个叶子节点, 也选择了该叶节点的父亲节点时, 此时叶子节点对答案的贡献减少1.
若选择了num个叶子节点, 它们有同一个父亲节点p, 此时我选择p节点后, 相当于对答案的贡献减少了num.
再推广, 如果对于一个节点q, 其子树内选择了num个节点, 我选择节点q后, 对答案贡献减少了num.
由此我们得出结论: 当选择非叶节点时, 我们要考虑其深度和其子树内选择的点的数目两个因素.
其实我们也不难想到, 当我们考虑是否选择某个节点时, 该节点子树内所有的节点应都已经被选择. 否则我选择任一子树内部节点, 都比当前节点更优.
由此我们可以得出每个节点对答案贡献公式: 该节点贡献 = 该节点深度 - 其子树节点个数
我们贪心选择前m个贡献最大的节点即可.
AC代码
#include <bits/stdc++.h>
#define rep(i, n) for (int i = 1; i <= (n); ++i)
using namespace std;
typedef long long ll;
const int N = 2E5 + 10;
vector<int> edge[N];
int qaq[N], sz[N];
void dfs(int x = 1, int fa = 0, int depth = 0) {
qaq[x] = depth; sz[x] = 1;
for (auto& to : edge[x]) {
if (to == fa) continue;
dfs(to, x, depth + 1);
sz[x] += sz[to];
}
qaq[x] -= sz[x] - 1;
}
int main()
{
int n, m; cin >> n >> m;
rep(i, n - 1) {
int a, b; scanf("%d %d", &a, &b);
edge[a].push_back(b);
edge[b].push_back(a);
}
dfs();
sort(qaq + 1, qaq + 1 + n, greater<>());
ll res = 0;
rep(i, m) res += qaq[i];
cout << res << endl;
return 0;
}