树上依赖性背包

问题 1 给定一棵 \(n\) 个节点的有根树,节点 \(u\) 有价值 \(a_u\)。选定包含根的点数 \(=k\) 的连通块,最大化总价值并输出。

设 \(f_{u,i}\) 表示 \(u\) 子树内选 \(i\) 个点的最大价值,若 \(i \ge 1\) 钦定 \(u\) 必选。

初值 \(f_{u,0}=0,f_{u,1}=a_u\)。合并 \(v\) 是 \((\max,+)\) 卷积

\[f_{u,i+j} \gets f_{u,i} +f_{v,j},\quad 1 \le i \le \text{siz}_u \land j \le \text{siz}_v \land i+j \le k \]

写成代码形如

int n, k, a[N], f[N][N], siz[N];
vector<int> e[N];
void dfs(int u) {
    siz[u] = 1, f[u][1] = a[u];
    for (int v : e[u]) {
        dfs(v);
        vector<int> f0(f[u], f[u] + siz[u] + 1);
        for (int i = 1; i <= siz[u] && i <= k; i++)
            for (int j = 0; j <= siz[v] && i + j <= k; j++)
                chkmax(f[u][i+j], f0[i] + f[v][j]);
        siz[u] += siz[v];
    }
}

定理 1 限制 \(i \le \text{siz}_u \land j \le \text{siz}_v\) 时,复杂度 \(O(n^2)\)。

证明 所有 \((x,y)\) 点对只在 \(\text{lca}(x,y)\) 处贡献一次。

定理 2 额外限制 \(i+j \le k\) 时,复杂度 \(O(nk)\)。

证明 按合并顺序取 DFS 序。合并时已处理的 \(u\) 部分是连续区间,\(v\) 部分接在 \(u\) 后面。

设 \(x\) 是 \(u\) 部分倒数第 \(i\) 位,\(y\) 是 \(v\) 部分正数第 \(j\) 位,需要 \(i+j-1<k\)。点对 \((x,y)\) 只有 \(O(nk)\) 个。


问题 2 给定一棵 \(n\) 个节点的有根树,节点 \(u\) 有价值 \(a_u\) 和体积 \(\mathbf{b_u}\)。选定包含根的总体积 \(=k\) 的连通块,最大化总价值并输出。

朴素树形背包的第二维 \(i\) 不再受 \(\text{siz}_u\) 的限制,复杂度 \(O(nk^2)\) 不可接受。

将树拍到 DFS 序,设 DFS 序为 \(d_1 \sim d_n\)。令 \(f_{i,j}\) 表示从 \(d_i\) 开始,体积恰好为 \(j\) 的最大价值。

设 \(u=d_i\),决策分两种:

  • 不选 \(u\),跳过 \(u\) 子树,转移 \(f_{i,j} \gets f_{i+\text{siz}_u,j}\)。
  • 选定 \(u\),继续向后,转移 \(f_{i,j} \gets f_{i+1,j-b_u}+a_u\)。

边界 \(f_{n+1,0}=0\),答案 \(f_{1,k}\),复杂度 \(O(nk)\)。

DFS 序上 DP 利用 \(i \to i+\text{siz}_u\) 跳过子树,从而处理了依赖性。

DFS 序 DP 的例题:P12444 和 P6326。

posted @ 2026-11-12 18:14  stripe_python  阅读(6)  评论(0)    收藏  举报