刷题笔记 21.8.5
题目来源:cf1466D
原题链接
题目翻译
一棵树,它有 n 个加权顶点。树是具有 n-1 条边的连通图。
让我们将其 k 着色定义为将 k 种颜色分配给边缘,以便每条边缘都分配有一种颜色。请注意,您不必使用所有 k 种颜色。
颜色为 x 的子图由来自原始树的这些边组成,这些边被指定为颜色 x,以及仅与至少一条这样的边相邻的顶点。所以在这样的子图中没有度数为 0 的顶点。
连接组件的值是其顶点的权重之和。让我们将子图的值定义为其连通分量的最大值。我们将假设空子图的值等于 0。
还有一个k-coloring的值,它等于所有k种颜色的子图的值之和。给定一棵树,对于从 1 到 n-1 的每个 k 计算 k 着色的最大值。
输入
在输入的第一行,有一个整数 t (1≤t≤105) 表示测试用例的数量。然后是 t 测试用例。
每个测试用例的第一行包含一个整数 n (2≤n≤105)。第二行由n个整数w1,w2,…,wn(0≤wi≤109)组成,wi等于第i个顶点的权重。在以下 n-1 行的每一行中,有两个整数 u, v (1≤u,v≤n) 描述顶点 u 和 v 之间的一条边。保证这些边形成一棵树。
所有测试用例中 n 的总和不会超过 2⋅105。
输出
对于每个测试用例,您的程序应该打印一行,其中包含用单个空格分隔的 n-1 个整数。一行中的第 i 个数字应该是树的第 i 个着色的最大值。
题目大意
就是给n个节点n个权重,再给n-1条边。用1~n-1种颜色去涂边最开始所有边都是一种颜色然后接下来每一种颜色只能涂一条边。要求每种颜色的边经过的节点权重加起来最大。输出1到n-1个颜色最大所得权重和。
解题思路 贪心 o(n)
第一个想法是用邻接表存一个无向图然后从大到小枚举每个节点,以这个数据量肯定会TLE 所以得另谋他路。模拟一遍过程发现,先把所有权重加起来得sum,所有出度大于1的节点加入一个数组e存权重w[i],出度有多少就减一加入e,然后从大到小排序枚举每一个加上sum就是答案。就是一个简单的贪心问题不要被题目吓到。
sum会爆int要用LL。
已ac源码
#include <iostream>
#include <algorithm>
#include <cstring>
using namespace std;
typedef long long ll;
const int N = 2e5+10;
int t, n, w[N], s[N];
int e[N], cnt;
bool cmp(int a, int b){
return a > b;
}
int main ()
{
cin.tie(0);
ios::sync_with_stdio(false);
cin >> t;
while(t--){
memset(s, -1, sizeof s);
cnt = 0;
ll sum = 0;
cin >> n;
for(int i = 1; i <= n; i++){
cin >> w[i];
sum += w[i];
}
for(int i = 0; i < n - 1; i++){
int a, b;
cin >> a >> b;
s[a]++, s[b]++;
if(s[a] > 0) e[cnt++] = w[a];
if(s[b] > 0) e[cnt++] = w[b];
}
sort(e, e + cnt, cmp);
cout << sum << ' ';
for(int i = 0; i < cnt; i++){
sum += e[i];
cout << sum << ' ';
}
cout << endl;
}
return 0;
}