Loading

题解:P3177 [HAOI2015] 树上染色

[HAOI2015] 树上染色

思路:

先考虑怎么计算答案,后动态规划。

数学部分:

我们发现此题让我们计算最大收益,那我们考虑一种染色方案的收益是怎么算出来的:

显然总收益为所有边贡献之和,一条边对收益的贡献显然是经过它的次数与它的权值之积,那么总收益就是 \(\sum (w[i] \times time[i])\)

接下来考虑计算一条边经过次数:

我们设这一条边的两个端点分别为 \(u\)\(v\),用 \(size[i]\) 表示以 \(i\) 为根的子树的大小,\(black[i]\) 表示以 \(i\) 为根的子树的黑色点个数,\(white[i]\) 表示以 \(i\) 为根的子树的白色点个数,那么有:

\(time[i]=black[u] \times black[v] + white[u] \times white[v]\)

且恒有 \(size[x]=white[x]+black[x]\)

动态规划:

\(f[u][i]\) 表示 \(u\) 的子树中选 \(i\) 个黑色点所获得的最大收益,枚举点 \(u\) 的出边,设出边所到的点为 \(v\) 那么分别枚举以 \(u\)\(v\) 为根的子树选多少个黑色的点更新答案即可。

注意这里要由大到小枚举选点数量,因为转移是由小范围到大范围,如果由小到大枚举就会用到之前更新的答案导致重复计算。

动态规划不是本题重点,因此不过多赘述,请结合数学部分以及代码理解。

代码:

#include<bits/stdc++.h>
#define ll long long
using namespace std;
ll n, k;
ll head[2005], cnt;
ll f[2005][2005], size[2005];
struct node {
	ll v, w, ne;
}e[5005];
void add(ll u, ll v, ll w) {
	e[++cnt]={v, w, head[u]};
	head[u]=cnt;
}
void dfs(ll u, ll fa) {
	size[u]=1;
	for(int i=head[u], v; i; i=e[i].ne) {
		v=e[i].v;
		if(v==fa) continue;
		dfs(v, u);
		for(int x=size[u]; x>=0; x--) { //枚举u子树选多少点
			for(int y=size[v]; y>=0; y--) { //v子树选多少点
				if(x+y>k) continue; //越界跳过
				ll time=(y*(k-y))+((size[v]-y)*(n-k-size[v]+y));
        		/*(y*(k-y))表示black[v]*black[v]*/
         		/*((size[v]-y)*(n-k-size[v]+y))表示white[v]*white[u]*/
				f[u][x+y]=max(f[u][x+y], f[u][x]+f[v][y]+e[i].w*time); //f[u][x+y]由f[u][x]和f[v][y]转移过来,所以要倒序枚举
			}
		}
		size[u]+=size[v];
	}
}
int main() {
	cin >> n >> k;
	for(int i=1; i<n; i++) {
		ll u, v, w;
		cin >> u >> v >> w;
		add(u, v, w), add(v, u, w);
	}
	dfs(1, 0);
	cout << f[1][k];
	return 0;
}
posted @ 2024-11-18 07:26  Anins  阅读(28)  评论(0)    收藏  举报