D. The Omnipotent Monster Killer
题目大意:
有一棵树,树节点数不超过
- 计算当前剩余所有的点权和,累计到
中 - 任选若干个互不相邻的节点并移除
重复这个操作直到树上没有任何节点为止,最小化
整体感觉这道题带来的启发很多,值得慢慢细品。
首先第一个误区以为可以贪心的两轮操作结束,比如:
先移除2 4,再移除1 3。
但是很容易举出反例:
显然要先移除4 5,再移除一个2,最后移除剩余的1,需要三轮才能完成。
三轮能否确定结束呢?如果是链式结构确实是可以的,因为链式结构中,任意节点最多只有两个节点相邻,这三者互不相同也只有三轮操作。
而在树上,某个节点N相邻可能有n个
然后是第二个误区,是否每轮选择树上最大独立集?即任选互不相邻节点,树形dp求权重和最大的选择。
这个可以解决上面1-2-3-4和5-2-1-4两种情况。
如果有多轮,似乎也可以解决,然而这也是不对的,简单反例如下:
第一轮最大独立集是(5,6),然而剩余的(3,4)由于是相邻的,只能先选4再选3,
而选择(4,6),则有
综上我们得出两个结论,第一需要多轮操作才可能最小化
此时一个关键问题摆在了我们面前,我们最多可能需要多少轮操作呢?
构造多轮的树结构不是那么容易,赛时也挺难悟到,这里给出一个建树方式
-
初始只有两个节点1,此时我们需要一轮操作
-
添加一个节点2,此时我们需要两轮操作
-
在1和2节点上添加4和5,此时由于我们需要先移除(4,5),所以需要三轮操作
-
下面是递推的构造,在之前出现的所有节点上,添加(8-11),此时第一轮移除(8-11)是最优的,加上之前的三轮,我们就需要四轮操作
-
...重复下去,每轮添加的节点个数是
,节点权值分别是 。
这样需要x轮操作移除所有节点的情况下,节点总数需要
这是第一个难点,需要能推算到至多需要的轮数是
讨论真正的做法前,我们需要反思以下:
之前常做的树形dp往往是对一个节点的操作是选或者不选这种两个状态的转移,典型就是树的最大独立集。
就算是多节点多状态也往往是题意里推导出的带有意义的固定个数,比如968. 监控二叉树
这导致很容易把树形dp中dp的内涵忽略,对本题而言,虽然是树形dp,但是节点却是有
因此第二个难点就是,要能发现树形dp的状态需要定义为数组
然后如何进行状态转移呢?每个节点需要基于所有子节点的状态进行转移
这是难点三,推导转移方程。每个节点的初始都是一致的,即每轮的自身代价。
这里假设节点i有k个子节点,转移中,是对于每个自身轮数,需要依次累加子节点不同轮数的最小
即
简单枚举每个
整体复杂度
不过这里有个经典小技巧用于处理这个问题。
对于每个
然后对于每个
所以整体复杂度可以降为
核心代码如下(未使用该技巧,本题没卡log)
long[][] f = new long[n][];
for(int i = 0; i < n; i++) f[i] = new long[T];
void DFS(int u, int fa)
{
for(int k = 0; k < T; k++)
{
f[u][k] = (k + 1) * w[u];
}
foreach(int v in g[u])
{
if(v == fa) continue;
DFS(v, u);
for(int k = 0; k < T; k++)
{
long min = long.MaxValue;
for(int s = 0; s < T; s++)
{
if(s != k) min = Math.Min(min, f[v][s]);
}
f[u][k] += min;
}
}
}
DFS(0, -1);
long ans = long.MaxValue;
for(int i = 0; i < T; i++) ans = Math.Min(f[0][i], ans);
Print(ans);
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】