dfs 序 DP

DFS 序 DP:与合并子树的树形背包相对。

状态设计通常为 \(dp(i,S)\) 表示考虑了 dfs 序的前 \(i\) 个点,各种状态为 \(S\) 的某种属性。

  • 优势:每次只新加入一个点。

  • 劣势:要处理往回跳的情况(dfs 序增加 \(1\),可能是切换子树了)。

  • 常用优化技巧:重链剖分,在先重子 dfs 序上处理。

例题一:HDU6566 The Hanged Man(独立集背包问题)

直接正常属性背包 \(dp(i,j,0/1)\) 表示 \(i\) 子树选出重量和为 \(j\) 的独立集,且 \(i\) 是否选了,的最大价值。于是获得了一个复杂度 \(O(nm^2)\) 的做法,然而没什么优化空间。

另外一个想法是 DFS 序 DP:\(dp(i,j)\) 表示考虑到 dfs 序为 \(i\) 的结点,选择的重量和为 \(j\)。这样走向儿子的时候就不用记录 \(i\) 的选择状态,但是回溯的时候需要记录所有祖先的选择状态 …… 所以要升成 \(dp(i,j,S)\) 多记录祖先们选没选,于是我们获得了复杂度 \(O(nm2^n)\) 的优秀做法!

接下来是一个非常牛的优化。上面是对任意 dfs 序 DP,能不能钦定一个 dfs 序从而优化呢?我们做轻重链剖分,按后重子深搜序 DP,那么一个点回溯的时候需要考虑的结点一定是\(rt-i\) 路径上某条轻边的父亲!因为到根经过的轻边只有 \(O(\log n)\) 条,对这些结点状压的复杂度是 \(O(2^{\log n})=O(n)\) 的!

于是复杂度优化为 \(O(n^2m)\),可以通过。

例题二:P3780 苹果树。

选一个包含根的连通块,\(t-h\le k\),最大价值。

算法明显是 \(O(nk)\) 的。

【简化版】

因为 \(t-h\le k\) 这个条件过于阴间了,尝试解决简化版 \(t\le k\)

\(a_v=1\)

即使是简化版,也先考虑解决 \(a_v=1\) 的特殊情况。这种情况,每个结点只有选和不选两种情况。

采用 dfn 序 DP。\(dp[i][j]\) 表示目前考虑完 \(dfn\le i-1\) 的结点(\(i\) 待定),已经选了 \(j\) 个结点,且强制 \(i\) 的祖先们都选了,最大权值是多少。

刷表法转移,考虑 \(i\) 选不选。若 \(i\) 选,转移到 \(dp[i+1][j+1]\);若 \(i\) 不选,记 \(big[x]\)\(x\) 子树中 dfn 的最大值,转移到 \(dp[big[i]+1][j]\)

答案取 \(\max dp[n+1][0\sim n]\)。状态 \(O(nk)\),转移 \(O(1)\),总共 \(O(nk)\) 的复杂度。

\(a_v\) 任意】

然后解决简化版的一般情况。状态描述不变,考虑怎么优化转移过程。

先考虑朴素的转移是怎么做的。若选 \(0\) 个,转移到 \(dp[big[i]+1][j]\);否则 \(dp[i][j]+w_i\times c\rightarrow dp[i+1][j+c]\),要求 \(c\ge 1\)

不选的情况不用动,只需要优化选的情况。

\(g_j=\max_{1\le j-c\le a_i,c\ge 1}\{dp[i][c]+w_i\cdot (j-c)\}=w_i\cdot j+\max_{1\le j-c\le a_i,c\ge 1} dp[i][c]-w_i\cdot c\)。把 \(dp[i][c]-w_i\cdot c\) 视作 \(f[c]\),就是经典的滑动窗口问题,可以 \(O(k)\) 求出 \(g_0\sim g_k\)
然后用 \(g_k\) 转移 \(dp[i+1][k]\)

还是 \(O(nk)\) 的。

【原版】

\(h\) 可以看作是 "允许让一条链上的点免费取一个"。

\(a_v=1\)

新建一个 dfn2 序,和上面的 dfn 对儿子的访问顺序完全相反。然后新建一个 \(dp2[][]\) 按照 dfn2 的顺序 DP,状态定义和 \(dp[][]\) 一样。

在求出两个数组之后,枚举结点 \(v\) 为 "免费链" 的最下端结点,枚举 \(j1\) 为 "dfn 比 \(v\) 小的选的个数",\(dp[v][j1]+dp2[v][j2]+w_v\) 即为 \(v\) 的最大可能贡献。其中 \(j2\) 满足 \(j1-(d[v]-1)+j2-(d[v]-1)=k\)
之所以加上 \(w_v\),是因为两个状态定义里 \(v\) 都是待定的,所以在最后把这个免费的 \(v\) 的权值加上。

\(a_v\) 任意】

这里不能按上面做的原因,是 \(v\) 的祖先可以选不止一个,在 \(dp[v]\) 中是一种选法,在 \(dp2[v]\) 中又是不同的选法,情况不同当然不能直接合并。

那能不能让 \(v\) 的祖先的 \(a=1\) 呢?就相当于除了叶子结点的 \(a\) 都等于 \(1\)

事实上是可以的,我们拆点,若 \(a_i>1\),给 \(i\) 额外拆出一个结点 \(new\) 作为儿子,\(a[new]=a[i]-1,w[new]=w[i]\) 然后 \(a[i]\leftarrow 1\)。这是等价的。

posted @ 2025-11-16 09:58  FLY_lai  阅读(26)  评论(0)    收藏  举报