「Tricks」整体DP
不太了解这个东西的具体定义是什么,总之应该是一个用数据结构维护 DP 状态的某几个维度的 trick 吧。
事实上你可以把这篇 post 理解为三个题的解集。
先直接来看 noi2020 - Destiny 这个题。
给定一棵树
T = (V, E) 和点对集合\mathcal Q \subseteq V \times V ,满足对于所有(u, v) \in \mathcal Q ,都有u \neq v ,并且u 是v 在树T 上的祖先。其中V 和E 分别代表树T 的结点集和边集。求有多少个不同的函数f :E \to \{0, 1\} (将每条边e \in E 的f(e) 值置为0 或1 ),满足对于任何(u, v) \in \mathcal Q ,都存在u 到v 路径上的一条边e 使得f(e) = 1 。由于答案可能非常大,你只需要输出结果对998,244,353 (一个素数)取模的结果。
我们略过 DP 的过程,直接给出其定义
令
如果我们考虑把 DP 的第二维度状态放到线段树上,那么子树的合并就可以放到线段树上去做,即使用线段树的合并 trick 来做 DP。
我们来看看合并的具体过程。贴近实现,我们令
有这样几种情形需要探讨。
于是得到解决,参考实现。
再来看 pkuwc2018 - Minimax 这个题。
给出一棵有
n 的结点的二叉有根树,并给出其叶子结点的权值,对于一个非叶子结点,其权值有p_i 概率取得儿子中的最大值,1-p_i 的概率取得最小值。令
\{v_{i}\} 表示最终根结点(1 -th 结点)的所有可能取值(升序排列),其个数记为m ,每一个v_i 取得的概率记为r_i ,将其按照{\rm hash}(i)=i\times v_i\times r_i 的规则求出\sum_i{\rm hash}(i) 。
与上一题类(完 全)似(一 致)地,同样略过 DP 的过程,给出其定义
令
同样考虑把 DP 的第二维度状态放到线段树上,在线段树合并时维护
参考实现。
最后来看到 codeforces - 671D / Roads in Yusland。
给定一棵
n 个点的以1 为根的树。有m 条路径(x,y) ,保证y 是x 或x 的祖先,每条路径有一个权值。你要在这些路径中选择若干条路径,使它们能覆盖每条边,同时权值和最小。
这题的平方 DP 都有一定难度……看了 duyi 的题解挺久才理解……不过如果做过 noi2020 - Destiny 的话应该会好很多。
称
转移即
然后考虑线段树合并优化,你需要支持区间增量 & 全局查询最小值 & 合并(与此同时区间取
考虑以时间换空间,我们采用权值平衡树 & 启发式合并来解决(参考实现中给出的是 std::set)。
平衡树上每个结点维护一个 2-tuple
- 区间增量:如果及时把
i>{\rm dep}(x) 的元素弹出,这就变成了全局增量,直接维护即可; - 全局查询最小值:这个是本题最妙的地方,因为关键字的选取,平衡树无法简单地取出最小值,我们需要更多的性质。注意到
\forall j<k,s.t.f(x,j)<f(x,k) ,此时的k 都是无用的,可以直接删除,这得出了此题的单调性。如此全局最小值就是平衡树上最右边的结点; - 合并:启发式合并即可;
- 单点取
\min :相当于插入操作,直接来即可,需要注意一些不是特别细的细节(雾)。
如此时间复杂度退成了
参考实现。