E. Paint the Tree(树形dp+贪心)

题目

题意:

    给定一棵带权树,你必须给每个点涂上k种颜色,每种颜色至多用两次。对于一条边连着的两个点,如果有一种颜色相同,那么就会对答案产生边权的贡献。要求分配颜色使得边权最大。
     1 ≤ n , k ≤ 5 ⋅ 1 0 5 , 1 ≤ u i , v i ≤ n , u i ≠ v i , 1 ≤ w i ≤ 1 0 5 1≤n,k≤5⋅10^5,1≤u_i,v_i≤n , u_i≠v_i, 1≤w_i≤10^5 1n,k5105,1ui,vin,ui=vi,1wi105

分析:

    理解一下题意就是每个点最多有k个机会与相邻的点相连对答案产生贡献。对于一个点来说,它要与另一个点相连,那么另一个点能连的边数就少了,所以不能贪心的选择边权大的边。
    我们发现给定的是一棵树,贪心不行自然就想dp。又是一棵树,就可以往树形dp的方向上想。对于一个点,它与其父亲节点连与不连两种选择。dp[i][0]表示与父亲不连以i为根的子树的最大值。那么有k个可连,连一个产生的多出来的贡献为dp[t][1]+v-dp[t][0]。排序选最大的k个即可。dp[i][1]选k-1个。

#include <iostream>
#include <vector>
#include <algorithm> 
using namespace std;

typedef long long ll;

struct node{
	int id,v;
	node(int a,int b)
	{
		id = a;
		v = b;
	}
};

vector<node> g[500005];

ll dp[500005][2];
int tot;

void dfs(int x,int fa)
{
	vector<ll> add;
	dp[x][1] = dp[x][0] = 0;
	for (int i = 0; i < g[x].size(); i++)
	{
		node t = g[x][i];
		if( t.id == fa ) continue;
		dfs(t.id,x);
		dp[x][0] += dp[t.id][0];
		add.push_back(dp[t.id][1]+t.v-dp[t.id][0]); 
	}
	dp[x][1] = dp[x][0];
	sort(add.begin(),add.end());
	int rest1 = tot;
	int rest2 = tot - 1;
	for (int i = add.size() - 1; i >= 0; i--)
	{
		if( add[i] < 0 ) break;
		if( rest1 )
		{
			dp[x][0] += add[i];
			rest1 --; 
		}
		if( rest2 )
		{
			dp[x][1] += add[i];
			rest2 --;
		}
	}
}

int main()
{
	ios::sync_with_stdio(false);
	cin.tie(0);
	int t;
	cin >> t;
	while( t-- )
	{
		int n,k;
		cin >> n >> k;
		tot = k;
		for (int i = 1; i <= n; i++) g[i].clear();
		for (int i = 1; i < n; i++)
		{
			int x,y,v;
			cin >> x >> y >> v;
			g[x].push_back(node(y,v));
			g[y].push_back(node(x,v));  
		}
		dfs(1,0);
		cout << dp[1][0] << '\n';
	}
	return 0;
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值