社区讨论
为什么以及怎样用上下界优化保证平方复杂度
P3177[HAOI2015] 树上染色参与者 5已保存回复 9
讨论操作
快速查看讨论及其快照的属性,并进行相关操作。
- 当前回复
- 9 条
- 当前快照
- 1 份
- 快照标识符
- @lo13be8d
- 此快照首次捕获于
- 2023/10/22 14:30 2 年前
- 此快照最后确认于
- 2024/07/12 19:34 2 年前
本题是典型的树上背包,正确的算法复杂度是,其中是树的大小,是可选的物品数量。subtask1是用来卡的错误写法的(不开O2的情况下)。在我正确通过了subtask1之后,我终于想通了为啥以及如何正确地做上下界优化,特发此贴以帮助后面卡在这里的人思考。
正如很多别的复杂度分析的文章指出,只要单个子树的算法复杂度是的,那么在dp数组限制上界到之后,整体算法复杂度就是。这一点不再赘述。本贴核心在于怎么控制子树复杂度到。
暂且不考虑dp数组上界控制到的优化。每个子树的dp数组上界开到,即该子树大小;于是,。设对大小为的树复杂度是,那么应该有,其中是合并步的复杂度。
一种(可能)正确的写法如下。
CPPint sz = 1;
for (const auto& [nt, w]: gr[cr]) {
if (nt==pa) continue;
auto [cnt, ndp] = dfs(dfs, nt, cr);
for (int i = min(sz+cnt, k); i>=0; --i) {
for (int j = max(0, i-min(sz, k)); j<cnt && j<=i; ++j) {
dp[i] = max(dp[i], dp[i-j]+ndp[j]+1LL*w*(j*(k-j)+(cnt-j)*(n-cnt-k+j)));
}
}
}
为什么这样是对的?因为(继续忽略)我们需要把循环控制在内,这是一个平行四边形(请自行画图),所以总的大小是。适当牺牲严谨性但便于表达,扔掉括号里的,这样只会扩大后面大O里的系数。那么,可归纳证明。因为,。在加上一堆一次项和常数项后,不会改变这个阶的判断。
一个常见的错误写法如下:
CPPint sz = 1;
for (const auto& [nt, w]: gr[cr]) {
if (nt==pa) continue;
auto [cnt, ndp] = dfs(dfs, nt, cr);
for (int i = min(sz+cnt, k); i>=0; --i) {
for (int j = 0; j<cnt && j<=i; ++j) {
dp[i] = max(dp[i], dp[i-j]+ndp[j]+1LL*w*(j*(k-j)+(cnt-j)*(n-cnt-k+j)));
}
}
}
它丢掉了j的下界控制。这样会给上面的平行四边形加个小三角,变成一个梯形。这个小小的三角形看似并没有改变合并步的复杂度,但是确实给整体复杂度提升了一个阶。此时算法复杂度大致是。如果树大致是平衡的,那么可以参考主定理,整体算法仍然是的。但是,对于极度不平衡的树,比如一个链,算法复杂度将满足,此时必然有,将完全无法接受。
所以请控制好上下界。
回复
共 9 条回复,欢迎继续交流。
正在加载回复...