题目链接:点我啊╭(╯^╰)╮
题目大意:
n
n
n 个点带边权的树,无限种颜色
每个点最多染
k
k
k 种颜色
每种颜色最多使用两次
若一条边所连两点染了同一种颜色,则加上该边权
求最大边权和
解题思路:
题目转化为对于一个点
最多计算
k
k
k 条与之相连的边的权
设
d
p
[
u
]
[
0
]
dp[u][0]
dp[u][0] 为点
u
u
u 与父节点不连边,子树最大的边权和,则
u
u
u 最多直接与子节点连
k
k
k 条边
设
d
p
[
u
]
[
1
]
dp[u][1]
dp[u][1] 为点
u
u
u 与父节点连边,子树最大的边权和,则
u
u
u 最多直接与子节点连
k
−
1
k-1
k−1 条边
那么对于点
u
u
u,计算最大的子树边权和,且
d
p
[
v
]
[
1
]
dp[v][1]
dp[v][1] 的个数不能超过
k
k
k 或
k
−
1
k-1
k−1
问题转化为有
n
n
n 件物品,每个物品有
a
a
a 和
b
b
b 两个价值,
b
b
b最多选取
k
k
k 次,求最大价值
考虑
d
p
dp
dp 会超时,这里采取另一种巧妙的做法
假设一开始全取
a
a
a,那么对于一个
a
a
a 变成
b
b
b,有差值
b
−
a
b-a
b−a,对这个差值排序
取较大的且
>
0
>0
>0 的前
k
k
k 或
k
−
1
k-1
k−1 个,加到全取的
t
o
t
tot
tot
核心:树形dp + 贪心
#include<bits/stdc++.h>
#define rint register int
#define deb(x) cerr<<#x<<" = "<<(x)<<'\n';
using namespace std;
typedef long long ll;
using pii = pair <int,int>;
const int maxn = 5e5 + 5;
int q, n, k;
vector <pii> g[maxn];
ll dp[maxn][2];
void dfs(int u, int f){
ll tot = 0;
vector <ll> v;
for(auto it : g[u]){
int to = it.first;
int w = it.second;
if(to == f) continue;
dfs(to, u);
tot += dp[to][0];
v.push_back(dp[to][1] + w - dp[to][0]);
}
sort(v.rbegin(), v.rend());
for(int i=0; i<min(int(v.size()), k); i++)
if(v[i] > 0) tot += v[i];
dp[u][0] = dp[u][1] = tot;
if(int(v.size()) >= k) if(v[k-1] > 0) dp[u][1] -= v[k-1];
}
int main() {
scanf("%d", &q);
while(q--){
scanf("%d%d", &n, &k);
for(int i=1; i<=n; i++) g[i].clear();
for(int i=1; i<n; i++) {
int u, v, w;
scanf("%d%d%d", &u, &v, &w);
g[u].push_back(pii(v, w));
g[v].push_back(pii(u, w));
}
dfs(1, 0);
printf("%lld\n", dp[1][0]);
}
}