题解:P3177 [HAOI2015] 树上染色

aimoyudexianyu

2024-11-15 09:19:41

Solution

[HAOI2015] 树上染色

思路:

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

数学部分:

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

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

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

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

且恒有 $size[x]=white[x]+black[x]$。 #### 动态规划: 用 $f[u][i]$ 表示 $u$ 的子树中选 $i$ 个黑色点所获得的最大收益,枚举点 $u$ 的出边,设出边所到的点为 $v$ 那么分别枚举以 $u$ 和 $v$ 为根的子树选多少个黑色的点更新答案即可。 注意这里要由大到小枚举选点数量,因为转移是由小范围到大范围,如果由小到大枚举就会用到之前更新的答案导致重复计算。 动态规划不是本题重点,因此不过多赘述,请结合数学部分以及代码理解。 ### 代码: ```cpp #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; } ```