本题树上分组背包的做法还是比较明显的,但是本人初做的时候没有敢这么写...
当时大致估算了下复杂度,背包转移二维,再加节点数,显然是n^3的算法了。。。
当然肯定跑不满,但当时感觉也只是常数问题.
无奈之下抱着也许有降维优化方法的想法去了题解区,发现大致的思路是差不多的。
第一篇题解的一个关键优化是每次u合并儿子son的背包时,两层循环中,外层只循环到u在遍历到son之前的叶节点总数,son只循环到son自己子树内的叶节点总数
,用刷表法实现。
具体到伪代码上就是
dfs{
int sum=0,t;
for(遍历u的儿子son){
t=dfs(son);
for(int i->sum)
for(int j->t) 刷表法更新背包
sum+=t;
}
return sum;
}
这么写似乎复杂度就是对的了(虽然我不会证复杂度,但是我卡不掉了...)
后面大部分的题解的优化是类似的,但是有一个很重要的不同:外层循环的遍历次数sum包括了当前儿子的叶节点个数
也就是:
dfs{
int sum=0,t;
for(遍历u的儿子son){
t=dfs(son);
sum+=t;
for(int i->sum)
for(int j->t) 填表法更新背包
}
return sum;
}
这样应该是为了写填表法方便,但是这样复杂度是假的!
可以构造出一个所有中转站为长链,最后一个中转站向所有用户连出菊花图的数据,在这种数据下,填表法的这种优化没有起效果,还是会被卡到n^3
数据生成器
以下为1-6篇题解在该数据下的表现
1: 0.087s
2: 2.316s
3: 1.545s
4: 2.205s
5: 2.882s
6: 2.004s
而且这已经是在O2的编译环境下了,我试了一篇不开O2,已经到了10s以上。
显然,除了第一篇题解,都无法处理这种情况
建议对这些题解进行修复 @mrsrz
另外,这道题向第一篇题解那么优化的严谨最差复杂度究竟是什么呢?蒟蒻想了很久没有想出来...
希望各位大佬指点迷津awa