CF1466-D. 13th Labour of Heracles
CF1466-D. 13th Labour of Heracles
题意:
给出一个由\(n\)个点构成的树,每个点都有一个权值。现在你可以用\(k,k\subset\)\([1, n]\)个颜色来给这棵树上的边涂色(这\(k\)种颜色不一定都要用上)。对于每种颜色都有一个权重,权值是这样定义的:
将除了当前颜色\(col_i\)其他颜色的边删掉,剩余的边构成了一个个联通分量。对于任意一个联通分量我们设它的权重是\(w_i\),那么\(w_i\)就等于该联通分量中所有点的权值之和,则对于当前颜色,它的权值就是\(w_{col_i}=max\{w_1, w_2,\cdots,w_n\}\)
需要注意的是,这个联通分量是通过删边得到的,所以不会出现联通分量中只有一个点的情况。对于一个空的联通分量,我们认为它的权重为0。
现在又定义对于每个\(k\)也有一个权值,它等于它包含的所有颜色的权值之和,即\(w_k=\sum_{i=1}^kw_{col_i}\).
现在让你求出对于每个\(k\),它的权值\(w_k\)的最大值是多少。
思路:
首先非常显然的一个道理,我们不能涂完颜色后让一个或多个颜色被分割成多个联通分量然后取\(max\),这样无论如何都不可能比单一的联通分量的权值大。
我们可以试着模拟从涂一个颜色到涂两个颜色的过程, 变化的就是让其中一个点从只被计算一次变成被计算两次,对于从两个颜色到三个,三个到四个...都可以依次推广得到相同的结论。所以这里利用贪心的思想优先让权值大的点先被计算多次,再依次减少。
对于每一个点,他能够被计算的最多的次数就是它的度\((degree)\)。当\(k=1\)的时候,它的最大值只有一个就是每个点的权值之和。这时候每个边都会被计算一次,所有的边度都减去一,对于剩下的所有度还大于1的点,我们就按照上面说到的贪心方法进行计算就可以得到最终结果。
AC代码:
这段代码我借鉴了一些大佬的数据处理方法。
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
typedef long long ll;
const int maxn = 200005;
ll w[maxn], a[maxn << 1];
int deg[maxn], tot = 0;
int main() {
int cases, n;
scanf("%d", &cases);
while(cases--) {
tot = 0;
scanf("%d", &n);
for (int i = 0; i < n; i++) {
deg[i + 1] = 0;
scanf("%lld", &w[i + 1]);
}
int u, v;
for (int i = 1; i < n; i++) {
scanf("%d %d", &u, &v);
deg[u]++; deg[v]++;
}
ll ans = 0;
for (int i = 1; i <= n; i++) {
for (int j = 1; j < deg[i]; j++) {
a[tot++] = w[i];
}
ans += w[i];
}
std::sort(a, a + tot, std::greater<int>());
for (int i = 0; i < tot; i++) {
printf("%lld ", ans);
ans += a[i];
}
printf("%lld\n", ans);
}
return 0;
}