背景
题意
给定一棵 (n) 个节点的以 (1) 号点为根节点的树的 (n-1) 条边及权值 ((x_i,y_i,w_i)) ,求砍掉一些枝条后留下 (q) 条边的最大边权和。
解法
也不是树形 (dp) 模板。还是习题。
由于这棵树是二叉树,有一个优美的性质:某棵子树内保留的树枝只能由左右子树得到(这不是废话吗)。
考虑转换题意,砍掉一些枝条后留下 (q) 条边也即留下 (q+1) 个点且必须包含根。
于是就可以设 (f_{x,i}) 表示以 (x) 节点为根的子树留下 (i) 个点的最大边权和。
这个时候就要考虑继续转换题目了。由于根必选,所以 (n-1) 条边完全可以转化成剩下 (n-1) 个点的权值(每个点到它父亲的边的权值)。于是把最大边权和变成了最大点权和。
然后就可以高高兴兴转移了:设节点 (x) 的左儿子是 (l_x) ,右儿子是 (r_x) ,则 (f_{x,i}=max_limits{j in [0,i-1]} { f_{l_x,j}+f_{r_x,i-1-j} } +val_x) 。
至于 (l_x) 和 (r_x) 的求法,见下面的 (trick) 吧。
trick
一棵给定 (n-1) 条无向边的 (n(n leqslant 3 imes 10^3)) 点有根二叉树左右子节点的求法:
建立邻接矩阵,只不过这次要存成 (int) 形式, (g_{x,y}) 存的是 ((x,y)) 的边权。
从根开始建立,自顶向下递归。
每次递归到当前节点 (x) 时都遍历 (n) 个节点,找到第一个有边权的节点,将其作为 (l_x) ,然后递归处理该节点。返回后直接退出循环即可。找 (r_x) 时注意要重新遍历 (n) 个节点,找到第一个有边权的节点,将其作为 (r_x) ,然后递归处理该节点。
细节
注意两个边界: (f_{x,0}) 表示选 (0) 个节点,答案必然为 (0) ; (l_x=0) 且 (r_x=0) (即 (x) 是个叶节点)时没法选子树,直接返回本身权值即可。