dp 与 [dp 优化] 专题
[ABC134F] Permutation Oddness
思想:将排列计数看作二分图完美匹配,左部点视作位置,右部点视作值。
在 \(\mathrm{dp}\) 中体现这个过程,令 \(f_{i,j,k}\) 表示考虑左右部的前 \(i\) 个点,有 \(j\) 个点需要往后面匹配,由于完美匹配左右部点的失配点数量都是 \(j\),此时奇异度之和为 \(k\) 的方案数。分类讨论左部 \(i\) 和右部 \(i\) 是往前匹配还是往后匹配即可。
-
位置 \(i\) 和值 \(i\) 都往后匹配:\(f_{i+1,j+1,k-2i}\leftarrow f_{i,j,k}\)。
-
位置 \(i\) 和值 \(i\) 都往前匹配:各有 \(j\) 种选择,转移 \(f_{i+1,j-1,k+2i}\leftarrow j^2\cdot f_{i,j,k}\)。
-
位置 \(i\) 和值 \(i\) 一个往前一个往后:值和位置方案数相同,转移 \(f_{i+1,j-1,k}\leftarrow (j+j)\cdot f_{i,j,k}\)。
-
位置 \(i\) 和值 \(i\) 自己匹配:\(f_{i+1,j,k}\leftarrow f_{i,j,k}\)。
时间复杂度 \(O(n^4)\)。
LOJ 6069. 塔
考虑确定之后先找到最小满足要求的间隔,令 \(k=\sum\limits_{i=2}^n\max(p_i,p_{i-1})\),则这种排列所需要的最小空间就是 \((k+1)\),在 \((n+1)\) 个间隔中可以任意插,方案数就是 \(\binom{L-k+n}{n}\),如果我们求出 \(G_k\) 表示价值为 \(k\) 的排列个数,答案就是 \(\sum\limits_{k=0}^{n^2} G_k\binom{L-k+n}{n}\)。\(G_k\) 的求法是经典的连续段 dp,令 \(f_{i,j,k}\) 表示考虑前 \(i\) 个数,形成了 \(j\) 个连续段,连续段的意义就是在最终的排列中这些数是在一起的,此时价值为 \(k\) 的方案数,转移分成形成段、贴着段、合并段:
这部分 dp 时间复杂度为 \(O(n^4)\)。对组合数的部分,由于 \(L\) 很大用常规的方法求不了组合数,但是 \(n\) 很小,考虑直接从递推式 \(C(i,j)=C(i-1,j)+C(i-1,j-1)\) 出发,可以矩乘优化到 \(O(n^3 \log L)\)。
总时间复杂度 \(O(n^4+n^3\log L)\)。
[ARC106E] Medals
这种匹配的问题很难用一般方法解决,故考虑网络流。把员工看作左部点,天看作右部点,每个员工拆成 \(K\) 个点 \(i_1,i_2,\dots,i_K\),每个点向这个员工上班的天连边。问题转化成:在这个无限大的二分图中,求最小的前缀右部点个数使其与所有左部点构成完美匹配。二分答案,则右部点中 \([1,mid]\) 的点被保留。经典的,考虑 Hall 定理,设 \(N(S)\) 表示左部点集合 \(S\) 对应的右部点集,那么存在完美匹配等价于 \(\forall S,|N(S)|\ge |S|\)。\(S\) 看似有很多,但其实对于每个员工最紧的限制一定要么不选要么全部选上这 \(K\) 个点,因此可以不拆点,将条件转化成 \(k\cdot |S|\le |N(S)|\),此时 \(S\) 的数量已经到了可以暴力枚举的 \(2^n\)。问题在于如何快速求 \(|N(S)|\)。可以预处理出每一天上班的人的集合 \(W_i\),那么对员工集合 \(S\),第 \(i\) 天有人上班等价于 \((W_i\cap S)\ne \varnothing\),交非空容斥成交为空,于是直接对 \(W_i\) 做高维前缀和即可,判定条件形如 \(K\cdot \mathrm{popc}(S)\le mid-F_{U\oplus S}\)。
最后一个问题,二分的上界。我们声称答案不超过 \((2KN)\),因为对于每个人,在 \((2K)\) 的时间内一定至少有 \(K\) 天在上班,对每个人独立考虑就得到了这个上界,时间复杂度 \(O(N^2K+2^NN\log (NK))\)。
CF1768F Wonderful Jump
如果用常规的单调栈结合斜优优化,会有 \(i,a_i\) 两个参数很难处理。
找一些性质。注意到值域只有 \(n\),而 \(j-i\) 的贡献却是平方级别的。考虑一个一个跳,这样距离为 \(\ell\) 的两个位置距离的上界就是 \(n\ell\),那么有效的转移需要满足:
出现了 \(\frac{n}{\ell}\),考虑根号分治。分类转移:
-
\(\ell\le \sqrt{N}\),可以直接枚举 \(j\) 进行转移。
-
\(\ell>\sqrt{N}\),则 \(\min(j,i)\le \sqrt{N}\),考虑如何利用这一点:
\(\mathbf{Lemma.}\) 最优转移 \(j\to i\) 中一定不存在 \(j<k<i\) 使得 \(a_k=\mathrm{min}(j,i)\)。
核心是 \(a^2+b^2\le (a+b)^2\)。可以导出 \(w(j,k)+w(k,i)\le w(j,i)\)。那么最小值要么在 \(j\) 要么在 \(i\)。
- 最小值在 \(j\),\(j\) 一定是左边第一个出现的 \(x\),直接枚举是 \(O(\sqrt{N})\) 的。
- 最小值在 \(i\),直接暴力往左枚举转移,直到遇到 \(a_j\le a_i\) 停下。这样是正确的因为对于每个 \(a_j\),只有可能被 \(x>a_j\) 的数转移到,并且对每个 \(x\) 只能取到最靠左的那个转移,因此均摊下来就是 \(O(N\sqrt{N})\)。
于是总时间复杂度 \(O(N\sqrt{N})\)。
[AGC009E] Eternal Average
把合并看作新建节点,这样会形成一棵 \(k\) 叉树,每个叶子子有权值 \(0/1\),每个点的权值是儿子权值和除 \(k\),最后得到的数就是根节点的权值。于是根节点的权值就是 \(\sum\limits_{col_u=1} k^{-\mathrm{dep}(u)}\),显然每个位置地位相同,因此我们只关注每一层的叶子有几个 \(1\),因此不妨将同层的 \(1\) 调整顺序到最右边。一个比较直观的想法就是直接数位 dp,按层处理叶子并记录进位,但这样会算重,因为当前层一个儿子全是 \(1\) 叶子的节点等价于更高层的一个 \(1\) 叶子,考虑将这些节点全部缩掉,就是将这个节点的所有儿子删去,然后这个点变成一个新的 \(1\) 叶子。

这样整个树一定会被压缩成右图所示的形式,此时我们仅关注这棵树的高度和剩余的 \(1\) 个数,如果知道这两个信息就可以背包求出方案数。不妨枚举缩掉了 \(x\) 个子树,令 \(M=\frac{n+m-1}{k-1}\) 也就是在不考虑缩子树时能够到达的最深深度,那么每缩一次我们其实丢失了一个深度,因此最后的深度是 \(M-x\),每缩一次丢失 \((k-1)\) 个 \(1\),那么剩余 \(1\) 的个数就是 \(m-x(k-1)\)。令 \(f_{i,j}\) 表示深度为 \(i\),用了 \(j\) 个 \(1\) 的方案数,这就是一个简单的背包,那么最后答案就是 \(\sum\limits_x f_{M-x,m-x(k-1)}\)。时间复杂度 \(O(n^2)\)。

浙公网安备 33010602011771号