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];
    }
}
posted @ 2026-02-20 20:08  2025ing  阅读(1)  评论(0)    收藏  举报