Note 02 树形背包
在树上每一个节点,以每一个子树为一组物品,做一次分组背包,
通过 dfs 不断合并进行转移,动态记录上下界优化时间复杂度。
常见形式一:没有容量限制,容量即为子树节点数。
时间复杂度 \(O(n^2)\),理解成选点对,只会在其 LCA 处被计算一次。
void dfs(int u){
sz[u]=1;
for(auto v:G[u]){
dfs(v);
for(int i=0;i<=sz[u];i++)
for(int j=0;j<=sz[v];j++)
//do something
sz[u]+=sz[v];
}
}
常见形式二:有容量限制,容量即为子树节点数。
时间复杂度 \(O(nm)\)。
定义:
- 当一颗树大小 \(\ge m\),且其自身所有儿子树大小 \(<m\),称其为耗子树。
容易发现,耗子树之间没有交集,且至多有 \(\frac nm\) 棵。- 当一颗树删去一个子树后缀后满足“耗子树”定义,称其为半吊子耗子树。
容易发现,半吊子耗子树满足上述性质。- 下设 \(v\) 为 \(u\) 儿子,当前已合并子树为 \(sz_u\)。
证明:对于执行了 \(n\) 次的这些双重循环进行分类。
- \(sz_u,sz_v\ge m\)
发现 \(u,v\) 之中都至少包含一棵耗子树。
合并之后其中至少一棵耗子树不能再用。
单次不超过 \(O(m^2)\) ,次数不超过 \(O(\frac nm)\),总时间复杂度 \(O(nm)\)。- \(sz_u+sz_v\ge m,sz_u<m,sz_v<m\)
当且仅当当前树是一颗耗子树或半吊子耗子树。
单次不超过 \(O(m^2)\) ,次数不超过 \(O(\frac nm)\),总时间复杂度 \(O(nm)\)。- \(sz_u+sz_v<m\)
应该没事吧……- \(sz_u\ge m,sz_v<m\)
忽然想到这样的 \(sz_v\) 两两不交,
时间复杂度 \(O(m\sum sz_v)<O(nm)\)。- \(sz_u<m,sz_v\ge m\)
同理。
void dfs(int u){
sz[u]=1;
for(auto v:G[u]){
dfs(v);
for(int i=0;i<=min(sz[u],m);i++)
for(int j=0;j<=min(sz[v],m-i);j++)
do something;
sz[u]+=sz[v];
}
}

浙公网安备 33010602011771号