do_while_true

一言(ヒトコト)

树上背包复杂度证明

参考

树上背包上下界优化。

考虑 \(v\)\(u\) 的儿子,现在 \(f_{u}\) 已经合并了若干个 \(v\) 前面的子树,(不包含 \(v\) 及以后的子树)其大小为 \(s_x\)\(v\) 子树的大小为 \(s_v\)

考虑转移 \(g_{u,i+j}\gets f_{u,i}+f_{v,j}\),然后 \(f_{u,i}\gets g_{i}\)

考虑 \(g\) 第二维的范围是 \([0,s_u+s_v]\)\(f_u\) 第二维的范围是 \([0,s_u]\)\(f_v\) 第二维的范围是 \([0,s_{v}]\)

枚举 \(i,j\) 使得一定满足这些范围即可。

考虑这次合并的时间复杂度 \(\mathcal{O}(s_us_v)\),那么在 \(u\) 此处合并子树的时间复杂度就是不断将 \(s_v\) 加入 \(s_u\) 产生 \(s_us_v\) 的代价,那么把 \(u\) 所有子树都合并起来的时间复杂度就是其所有子树大小(再加上它本身的一个 \(1\))两两乘积的和。

这个总和怎么计算?考虑每一个点对都会在 LCA 处贡献一个 \(1\),所以时间复杂度是 \(\mathcal{O}(n^2)\)


如果对背包的第二维进行一个 \(\leq k\) 的限制,复杂度是什么呢?

考虑称子树大小 \(\leq k\) 的为小子树,\(>k\) 的为大子树。

考虑小子树之间合并为极大的小子树(定义为再往父亲走一步就变为大子树)的复杂度,根据定义所有极大的小子树是不会互相包含的:

首先,合并得到一个大小为 \(x\) 的极大的小子树的复杂度是 \(\mathcal{O}(x^2)\),因为此时第二维没有了限制,相当于上一个问题。

假设极大的小子树的大小是 \(x_1,x_2,\cdots\),其和 \(\leq n\),它们的平方和最大就是每个 \(x\) 尽可能地取 \(k\) 也就是 \(k^2\times \frac{n}{k}=\mathcal{O}(nk)\)

然后考虑极大的小子树合并为极小的大子树(定义为极大的小子树的父亲)。

假设极大的小子树的大小是 \(x_1,x_2,\cdots\),其和 \(\leq n\),那么将它们合并到父亲的代价 \(\leq k\sum x_i\leq nk\)

最后是极小的大子树之间的合并,前面已经算完了极大的小子树之间都合并完的代价,现在只剩下了若干极小的大子树,并且它们互相不包含。所以它们个数最多 \(\frac{n}{k}\) 个,而每次合并的代价为 \(k^2\),总代价就是 \(\mathcal{O}(nk)\)

综上所述,该树上背包的复杂度为 \(\mathcal{O}(nk)\)

2023.8.3 upd 一个更简洁的证明

多个子树可以三度化掉(当然这只是把树上背包合并的过程显式体现而已)在 dfs 序上考虑,合并子树看作合并这 dfs 序上相邻的两个区间,看作前面选择一个长度为 \(x\) 的后缀,后面选择一个长度为 \(y\) 的前缀,满足 \(x+y\leq k\),它们拼成一个区间,方案数是多少。一个区间最多只会被拼出一次(也就是仅会在与它有交的子树都合并在一起的那一次被统计到),所以直接大力放缩成 dfs 序中所有长度 \(\leq k\) 的区间,即得 \(\mathcal{O}(nk)\) 的上界。

posted @ 2022-07-29 11:33  do_while_true  阅读(279)  评论(1编辑  收藏  举报