动态规划做题记录-3
动态规划做题记录-3
CF1290F. Making Shapes
考虑当我们确定每种向量需要使用的个数 \(c_i\) 之后,如果这些向量可以形成凸多边形,显然可以确定唯一的一个凸多边形,因为题目中要求向量的顺序必须是逆时针,那么按照极角排序就是你选择向量的顺序。现在只需要考虑向量是否可以形成凸多边形和这个凸多边形能不能平移到 \(m\times m\) 的范围里面。
假设第 \(i\) 个向量选择了 \(c_i\) 个,两个条件分别可以写成 \(\sum_{x_i>0}c_ix_i=-\sum_{x_i<0}c_ix_i,\sum_{y_i>0}c_iy_i=-\sum_{y_i<0}c_iy_i\) 和 \(\sum_{x_i>0}c_ix_i\le m,\sum_{y_i>0}c_iy_i\le m\)。针对第二个条件,我们可以想到数位 dp,将 \(\sum_{x_i>0}c_ix_i,\sum_{y_i>0}c_iy_i\) 和 \(m\) 写成二进制的形式即可维护,为了维护第一个限制,我们相应地同时维护 \(-\sum_{x_i<0}c_ix_i,-\sum_{y_i<0}c_iy_i\) 的二进制形式即可。于是我们可以设 \(f(p,xa,xb,ya,yb,xm,ym)\) 表示前考虑到前 \(k\) 位,当前 \(\sum_{x_i>0}c_ix_i\) 的进位是 \(xa\), \(-\sum_{x_i<0}c_ix_i\) 的进位是 \(xb\), \(\sum_{y_i>0}c_iy_i\) 的进位是 \(ya\), \(-\sum_{y_i<0}c_iy_i\) 的进位是 \(yb\),且 \(\sum_{x_i>0}c_ix_i,\sum_{y_i>0}c_iy_i\) 的前 \(k\) 位和 \(m\) 的前 \(k\) 位的大小关系是 \(xm,ym\),将后面全部填完且符合条件的方案数。转移可以考虑枚举 \(c_i\) 的这一位的值统计所有维护的值的进位,如果 \(\sum_{x_i>0}c_ix_i\) 和 \(-\sum_{x_i<0}c_ix_i\), \(\sum_{y_i>0}c_iy_i\) 和 \(-\sum_{y_i<0}c_iy_i\) 的二进制的这一位均相同,那么可以从相应的位置转移。
边界条件为 \(f(\lfloor\log m\rfloor+1,0,0,0,0,0,0)=1\),答案是 \(f(0,0,0,0,0,0,0)-1\),因为要排除 \(c_i\) 全是 \(0\) 的情况。复杂度是 \(O(4\times20^4\times5\times2^5\log m)\) 的,用记忆化实现可以跑得更快。
CF1292F. Nora's Toy Boxes
考虑如果对于 \(a_i\mid a_j\) 连接一条边 \(i\to j\),那么所有弱连通块之间的答案不会互相影响,因此我们可以对每一个连通块单独求解后简单合并答案。
考虑每一个弱连通块中那些没有入度的点,我们称这些点的集合为 \(S\),剩下的点的集合称为 \(T\)。显然 \(S\) 中的点不可能被删去,而 \(T\) 中的点在最后至少会剩一个点。我们可以证明在最优的方案下,\(T\) 中一定恰好剩下一个点:我们改删点为加点,我们先钦定一个点在 \(T\) 中,如果其他的点没有全部加入 \(T\) 中,说明 \(S\) 中还有点没有被扩展过,并且一定存在一个点使得它的出点有些被加入而有些没有被加入,接着我们可以扩展这个点,直到所有点被加入 \(T\) 中。
我们考虑用 dp 维护扩展的过程,令 \(f(i)\) 表示 \(S\) 中的点的扩展情况为 \(i\) 时,扩展剩余的点的方案数。我们相当于要将 \(T\) 中的所有点给出一个加点序列。考虑加点的过程,我们应该在 \(T\) 中选出一个点,使得它在 \(S\) 中的入点有一部分被扩展过而有一部分没有被扩展过,假设这个入点集合为 \(j\),那么应该从 \(i\cup j\) 转移过来。设 \(w(i)\) 表示 \(T\) 中入点集合 \(\subseteq i\) 的点的个数,那么这样会多出 \(w(i\cup j)-w(i)-1\) 个可以随意放置的点,剩余的位置总共有 \(w(U)-w(i)-1\) 个,于是方案数为 \(A_{w(U)-w(i)-1}^{w(i\cup j)-w(i)-1}\),每次枚举 \(T\) 中的每个点转移,复杂度是 \(O(n2^{|S|})\) 的。
考虑 \(S\) 中数的个数,我们发现,如果 \(T\) 中没有点我们根本不用讨论这个内容,因此 \(>30\) 的数是无用的,对于 \(\le 30\) 的数,我们想要找出最多的数使得他们两两不为倍数,显然选择 \(2\) 和所有非 \(1\) 奇数可以达到上限 \(15\)。更具体来说,假设值域上限为 \(m\),那么 \(|S|\le \frac{m}{4}\),因此复杂度是 \(O(n2^{\frac{m}{4}})\) 的。
CF1930G. Prefix Max Set Counting
考虑一个点在前缀最大值序列里的条件:
- 它的所有祖先节点的编号都要小于它。
- 没有 dfs 过子树 \(\text{max}\) 大于它的节点。
第一个条件是容易的,在 dfs 的时候特判就可以。第二个条件我们可以按照子树 \(\text{max}\) 对儿子排序,这样每一个点的贡献一定来自于 dfs 序在它之前的点。设 \(f(u)\) 表示在 dfs 序以 \(u\) 结尾的前缀的树上做原题,前缀最大值以 \(u\) 结尾的方案数。考虑什么样的 \(v<u\) 可以对 \(f(u)\) 造成贡献,设 \(d=\text{lca}(u,v)\)。
- \(d=v\),此时需要 \(v\) 是 \(u\) 的根链上除了 \(u\) 以外的最大值。
- \(d\ne v\),此时需要 \(v\) 是 \(d\) 在 \(v\) 方向上的儿子的子树的最大值。
因为 dfs 序上的顺序天然满足,所以我们只需要要考虑 \(v<u\) 的限制,可以用树状数组简单维护。设 \(m\) 是 \(u\) 的根链上非 \(u\) 的最大值。先特判掉 \(m<u\) 的情况,否则只需要找到所有 \(m\le v <u\) 的所有 \(f_v\) 的和即可,其中 \(v\) 是 \(u\) 的某个祖先或某个祖先的儿子的子树最大值。复杂度是 \(O(n\log n)\) 的。
JOISC2020F. 最古の遺跡 3
考虑当后缀 \([1,h]\) 的石柱均至少有一个时,前面的 \(\le h\) 的石柱一定会消失。我们可以称这个 \(h\) 为高度阈值,每种高度编号最大的柱子为标准柱,并且从这一点入手开始 dp。相同高度的石柱可以先看做本质不同,最后将答案除以 \(2^n\)。
不妨设 \(f(i,h)\) 表示填到第 \(i\) 个石柱,高度阈值为 \(h\) 且符合条件的方案数,同时记 \(c_0\) 为这个后缀消失的石柱的数量,\(c_1\) 表示这个后缀存在的石柱的数量,可以看出这个高度阈值和 \(\text{mex}\) 相似,我们有以下转移。
- 当前石柱最终消失,说明此时 \(a_i\le h\),总共有 \(2h\) 个取值,其中 \(h\) 个作为标准柱,\(c_0\) 个已经被占用,因此系数为 \(h-c_0\)。
- 当前石柱最终存在,我们继续讨论:
- \(a_i>h+1\),此时高度阈值将不变,具体的系数我们后面统一计算,因此系数为 \(1\)。
- \(a_i=h+1\),此时高度阈值变大,我们枚举具体高度阈值变到 \(k\),那么此时我们的系数可以分为
- 哪些柱子作为标准柱,即 \({c_1-h\choose k-h-1}\)。
- 当前这个柱子的高度取值,同样的,总共有 \(2(k-h)\) 种取值可能,其中 \(k-h-1\) 作为前面的标准柱,因此当前的取值方案为 \(k-h+1\)。
- 前面的所有柱子被震到符合条件的方案数 \(g_{k-h-1}\)。
那么这个内容可以在 \(O(n^3)\) 的时间复杂度内求出。接下来考虑 \(g\) 的求解,转移考虑枚举编号最小的点的高度,那么这个高度往上是可以随便取的,因为最后都会坠到目标高度。然后考虑前面哪些柱子高于它,哪些低于它,两者的贡献互不干扰,因此有
这个可以在 \(O(n^2)\) 的复杂度内预处理,因此总复杂度是 \(O(n^3)\)。
JOISC2022C. スペルミス
考虑题目中的 \(T_u\le T_v\) 的具体意义:
- \(u< v\) 时,要么 \(s_{u\cdots v}\) 都是相等的,要么 \(s_u>s_p\),其中 \(p\) 是第一个值和 \(s_u\) 不同的位置。
- \(u>v\) 时,要么 \(s_{v\cdots u}\) 都是相等的,要么 \(s_v<s_p\),其中 \(p\) 是第一个值和 \(s_v\) 不同的位置。
考虑从后往前 dp,设 \(f(i,c)\) 表示 \(s_i=c\) 且 \(s_i\) 为一段连续段的开头的方案数。计算 \(f(i,c)\) 的时候,考虑枚举上一个连续段的位置 \(i'\) 和 \(s_{i'}=c'\ne c\)。考虑什么情况下可以从 \(f(i',c')\) 转移过来:
- 如果存在 \(i\le u<i'<v\) 要求 \(T_u\le T_v\),那么 \(c>c'\)。
- 如果存在 \(i\le u <i'<v\) 要求 \(T_u\ge T_v\),那么 \(c<c'\)。
也就是对于 \(i'\),存在一个位置 \(p_1\) 使得所有 \(i\le p_1\),\(i'\) 无法做出 \(c< c'\) 的贡献,同理也存在一个位置 \(p_2\),使得所有 \(i\le p_2\),\(i'\) 无法做出 \(c>c'\) 的贡献。设 \(h_c\) 表示 \([i+1,n]\) 中的所有位置对 \(c\) 产生的贡献。考虑所有限制 \((i,v)\):
- 如果要求 \(T_i\le T_v\),那么所有 \(u<i'\le v\) 且仍产生 \(c<c'\) 的贡献的 \(i'\),将它的贡献删除,即令 \(h_c\) 减去 \(\sum_{c<c'}f(i',c')\)。
- 如果要求 \(T_i\ge T_v\),那么所有 \(u<i'\le v\) 且仍产生 \(c>c'\) 的贡献的 \(i'\),将它的贡献删除,即令 \(h_c\) 减去 \(\sum_{c>c'}f(i',c')\)。
答案的更新即 \(f(i,c)=h_c+1\),\(1\) 表示 \(s_{i\cdots n}\) 均相等的情况,接着统计答案对 \(h\) 的贡献,即 \(h_c\) 加上 \(\sum_{c\ne c'}f(i,c')\)。用并查集维护那些位置的贡献没有删除,复杂度是 \(O(n|\sum|)\)。
LG9312. [EGOI 2021] Lanterns / 灯笼
有一个 naive 的状态设计 \(f(l,r,i)\) 表示可以走到海拔 \([l,r]\) 内的山峰,并且当前在 \(i\),想要走完所有山峰的最小花费。这样我们可以唯一确定当前能够走到的区间 \([L,R]\),然后可以枚举这段区间内的所有灯笼进行转移,可以做到 \(O(n^3k)\)。
考虑我们现在复杂度较劣是因为一个区间内的内容我们需要三个参数才能确定,并且我们发现一个区间的答案会用多个状态同时表示,这显然不优。这启发我们用位置表示区间,令 \(f(u,v)\) 表示当前最低的海拔是灯笼 \(u\) 达到的,最高的海拔是灯笼 \(v\) 达到的,想要走完所有山峰的最小花费。可以看出我们依然可以求出当前可以走到的海拔区间 \([l,r]\) 和山峰区间 \([L,R]\)。此时复杂度可以降到 \(O(k^3)\),答案即为 \(f(i,i)+c_i\)
接下来考虑转移,因为如果海拔区间没有改变显然不优,所以无非就是三种转移:
- 拓展了 \(l\),从 \(f(u',v)+c_{u'}\) 转移,要求 \(a_{u'}<a_u\)。
- 拓展了 \(r\),从 \(f(u,v')+c_{v'}\) 转移,要求 \(b_{v'}>b_v\)。
- 同时拓展了 \(l,r\),从 \(f(k,k)+c_{k}\) 转移,要求 \(a_k<a_u,b_k>b_v\)。
考虑如何优化转移。关注到对于不变的 \(u\),如果 \(b_v<b_{v'}<b_{k}\),且 \(k\) 不能转移到 \(v'\),那么 \(k\) 不能转移到 \(v\)。这是因为 \((u,v)\) 对应的海拔区间被 \((u,v')\) 对应的海拔区间包含,又因为肯定都可以走到 \(p_u\),因此 \((u,v)\) 对应的山峰区间也被 \((u,v')\) 对应的山峰区间包含,所以如果 \(k\) 不在 \((u,v')\) 的山峰区间,肯定也不在 \((u,v)\) 的山峰区间,同理对不变的 \(v\) 也有这一点。这启发我们按照 \(a_u\) 递增,\(b_v\) 递减的顺序枚举 \(u,v\),这样如果某个 \(u',v'\) 不能作出贡献,直接将其从贡献队列删除即可。实现可以用小根堆维护 \(f(u,v')+c_{v'}\) 找到正确的贡献位置。这样可以优化到 \(O(k^2\log k)\),可以通过。
关于如何快速求出每个状态对应的山峰区间,我们可以预处理出每个灯笼向左和向右最远可以走到的位置,那么两个灯笼可以走到的区间就是两者的交。注意特判某个状态初始根本无法移动的情况。
JOISC2022I. 蟻と角砂糖
考虑题目要求蚂蚁去找方糖,不放假设一个方糖只能被最多一只蚂蚁找,那么求解的内容就变成了蚂蚁和方糖的最大匹配数,根据 Hall 定理的推论,我们可以得出这个值就是 \(|A|-\max_{S\subseteq A}\{|S|-|N(S)|\}\),考虑如何快速求解后半部分。
可以看出我们选择的蚂蚁是由多个区间 \([l_i,r_i]\) 组成的,假设 \(i\) 上面有 \(a_i\) 个蚂蚁和 \(s_i\) 个方糖,那么后半部分其实就是
考虑离散化后在线段树上实现这个内容,设 \(f(l,r,0/1,0/1)\) 表示线段树上 \([l,r]\) 区间的左端点和右端点是/否被选择时和式的最大值,那么合并两个区间相当于考虑是否需要合并区间,显然只有在 \((\sim,1),(1,\sim)\) 时才可能合并,此时的贡献还需要加上 \(\sum_{p=\text{mid}-L+1}^{\text{mid}+L}s_p\),显然这个贡献是非负的,因此合并之后一定不劣。根据转移式,我们顺带维护区间 \([l,r]\) 的 \(\sum_{p=r-L+1}^{r+L}s_p\) 即可。
接下来考虑修改带来的影响:
- 加入了 \(A\) 个蚂蚁,此时只会对某一个点的 \(a\) 造成影响,单点修改即可。
- 加入了 \(A\) 个方糖,考虑到相邻两个区间的间隔不会少于 \(2L\),否则合并起来一定更优。也就是说,一块方糖不会同时对两个区间造成贡献,因此此时对于 \([l,r]\in[X-L,X+L]\) 的区间,其 dp 值会减少 \(A\)。同时,对于维护的 \(\sum_{p=r-L+1}^{r+L}s_p\),所有 \(r\in[X-L,X+L)\) 的区间中,这些值会增加 \(A\)。用区间修改即可。
综上,复杂度是 \(O(n\log n)\) 的。
LG5281. [ZJOI2019] Minimax 搜索
考虑花费恰好为 \(k\) 的集合数较为难求,不如先转化成花费 \(\le k\) 后再进行差分。考虑先求出最开始时所有节点的权值,假设此时根节点的权值为 \(W\),那么编号为 \(W\) 的叶子节点的根链肯定也全为 \(W\),如果想要让根节点的权值改变,只需要让这条根链上的任何一个点的权值改变即可,于是我们考虑分开对每一个点求解其改变的集合数。
现在我们断掉了 \(W\) 的根链上的所有边得到了若干个连通块,假设其中的一个的根节点为 \(u\),在这个连通块中总共有 \(c\) 个叶子节点,那么在这里选择集合的总方案数是 \(\text{cnt}_u=2^c\)。设 \(f(u)\) 表示只考虑节点 \(u\) 的子树,\(u\) 的权值最终 \(<W\) 的集合数。考虑按照每个点在原树上的深度分类讨论:
- \(\text{dep}_u\bmod 2=1\),此时 \(u\) 取所有儿子节点的最大值,那么只需要所有的儿子节点的权值均 \(<W\) 即可,即 \(f(u)=\prod_{v\in\text{son}_u} f(v)\)。
- \(\text{dep}_u\bmod 2=0\),此时 \(u\) 取所有儿子节点的最小值,那么只需要有一个儿子节点的权值 \(<W\) 即可,用容斥简单得到 \(f(u)=\text{cnt}_u-\prod_{v\in\text{son}_u}(\text{cnt}_v-f_v)\)。
转移方程比较凌乱,但我们可以令 \(g(u)\) 在 \(\text{dep}_u\bmod 2=1\) 时表示 \(\text{cnt}_u-f(u)\),在 \(\text{dep}_u\bmod 2=0\) 时表示 \(f(u)\),那么转移方程就可以统一写作 \(g(u)=\text{cnt}_u-\prod_{v\in\text{son}_u}g(v)\)。初始值在所有叶子节点处,当根节点的深度为奇数时值为 \([u<W]+[u+k<W]\),否则为 \([u<W]+[u-k<W]\)。这是因为根据根节点的深度,我们为了使其改变,在调整的时候肯定需要尽可能对应向大或向小调。此时枚举 \(k\) 可以做到 \(O(n^2)\) 求解。
考虑每一次的转移实际上是完全相同的,只有初始值不同,而每一个点的初始值在 \(k\) 变化的最多改变一次,于是可以考虑动态 dp。对每个连通块树链剖分,那么转移是 \(g(u)=\text{cnt}_u-(\prod_{v\in\text{light}_u}g(v))g(\text{heavy}_u)\),可以看作 \(y=kx+b\) 的形式,并且这个形式有结合律,因此可以直接用线段树维护这个内容,复杂度是 \(O(n\log^2 n)\) 的。因为每一次跳轻边都要求逆元,所以即使使用全局平衡二叉树也是 \(O(n\log^2 n)\)。
实际实现的时候考虑到 \(g(u)\) 的取值可能为 \(0\),因此消除贡献的时候不能直接除,而是要统计所有非 \(0\) 的数的积和 \(0\) 的个数,然后特判是否有 \(0\) 之后才能得出正确的结果。
LG8334. [ZJOI2022] 深搜
这道题的思路和上一题较为相似,我们考虑先将求解内容转化为求解最小值 \(\ge V\) 的概率 \(p(V)\),这样最后的答案就是所有 \(p\) 的和。考虑将所有 \(\ge V\) 的点称为黑点,其他的点称为白点,并设 \(\text{col}_u\) 表示 \(u\) 的颜色(黑为 \(1\),白为 \(0\))。因为深搜走入一个子树,在走完之前不会出来,那么只要子树内有一个白点那么你就不可能走出这个子树。基于这个思路,我们设 \(\text{sub}_u\) 表示这个点的子树是不是全黑子树,\(\text{tot}_u\) 表示一个点有多少个非全黑子树,那么我们的要求就是在走入终点所在的子树之前不走入任何一个非全黑子树,假设终点在子树 \(v\) 中,根据经典概率结论,这个概率应该是 \(\frac{1}{\text{tot}_u+\text{sub}_v}\) 的。
于是我们可以设 \(f(u)\) 表示从 \(u\) 点出发走到所有子树内的节点只经过黑色结果的概率和,那么转移是显然的:
那么答案即为 \(\sum f(u)\)。现在我们考虑优化这个转移,考虑按照 \(\text{sub}_v\) 对转移分类,那么可以算出 \(\text{sub}_v=0\) 的 \(f\) 的和 \(f_0\) 和 \(\text{sub}_v=1\) 的 \(f\) 的和 \(f_1\),并且令 \(g(u)\) 表示从子树内所有点出发的概率和。那么转移可以写作
现在离散化后枚举 \(V\) 可以做到 \(O(n^2)\) 的复杂度。考虑从大到小枚举 \(V\) 的时候,每个点的 \(\text{col},\text{sub}\) 最多变化 \(1\) 次,改变的时候也只会影响父亲的 \(f_0,f_1,\text{tot}\),因此所有参数的总变化量是 \(O(n)\) 的,这启发我们考虑动态 dp。先进行树链剖分,将转移写作
记 \(A=[\text{col}_u=1]\frac{1}{\text{tot}_u+\text{sub}_{\text{heavy}_u}},B=[\text{col}_u=1](\frac{1}{\text{tot}_u}f_0(u)+\frac{1}{\text{tot}_u+1}f_1(u)+1),C=\sum_{v\in\text{light}_u}g(u)\),然后发现有矩阵的形式
于是可以用全局平衡二叉树实现动态 dp 做到 \(O(n\log n)\)。