【学习记录】DP 专题训练

· · 个人记录

原题单

剩余题单(包括课后题)

拜谢杨爹/bx/bx/bx

背包 DP

J - Cow Exhibition G

考虑把 S,F 中的一种看作费用,另一种看作收益,把所有值加个常数变成非负,转化为 01 背包求解,最后找到两种值都非负的最大值即可。

数位 DP

K - windy 数

用 dfs,由高位到低位填,参数为填到的位数 x,是否贴上界,是否有前导 0,上一位是 last,判断是否能填时要么有前导 0,要么跟上一位差的绝对值满足要求。加上没有前导 0 且不贴上界时对于 (x,last) 的解个数可以优化时间复杂度。

话说学长说更建议用递推写,感觉递推式子就是 (x,last) 的数组,还没有了解过。

L - 01串 Stringsobits

求符合条件的第 k 小数,考虑二分或倍增。这里二分一个数 x,求 [0,x] 中符合条件的数个数 s,以 s\ge k 来 check。求数的个数用数位 DP,参数里加入还能填的 1 的个数即可,初始为 (n,L,1)

N - Making Shapes

Too Hard. 大体是把每个向量选择的次数和各种和全都变成二进制进行数位 DP。

单调队列优化 DP

I - Mowing the Lawn G

不能安排连续超过 K 只奶牛,考虑转化成找出若干只奶牛不安排,也就是选出的任意两只奶牛之间距离不能超过 K+1。设 f_i 表示前 i 只奶牛选出的最小效率和,则 f_i=\min_{j=i-(k+1)}^{i-1}f_j+E_i

发现一定会用 f_j 合法的最小值更新,所以若 x<yf_x\ge f_yx 一定永远不优于 y,用双端队列维护单调队列,每次从队首弹出过期元素,从队尾弹出不优的元素,保证队内的 f_i 单调递减,每次用队首来转移即可。

M - PTA-Little Bird

f_i 为跳到 i 所需的最小代价,则 f_i=\min_{j=i-k}^{i-1}f_j+[d_i\ge d_j],考虑用单调队列,需要判断两个点的优劣。发现 f_x<f_yx 一定更优,若 f_x=f_y, 则 d_x>d_yx 一定更优。因为每次的代价仅为 1,所以这样是对的。把 (f_i,d_i,i) 放入单调队列,用队首更新即可。

树形 DP

E - Centroids

先找出重心,原重心一定合法。以重心 g 为根计算每个点的子树大小。若 {siz}_x=\frac n2,可以直接把这颗子树拆下来,把剩余的 \frac n2 接到指定的根上,保证会成为重心。所以 x 子树内的点均合法。

对于其他点,断开自己子树显然是不合法的,那么只能断开 g 其他子树中 siz 最大的 k,且直接接到 x 上。那么 siz_x,siz_k 均小于 \frac n2,只要 n-siz_x-siz_j\le\frac n2 即合法。预处理一下 g 子树的前缀后缀 siz 最大值,求子树 m 时用前缀和后缀的最大值作为 siz_k 即可。

G - 树上染色

考虑把贡献拆到边上,并在从子树合并到父节点时考虑进去。所以若子树 v 中有 j 个黑点,贡献的次数为 S=j(k-j)+(siz_v-j)(n-siz_v-(k-j))。合并入父节点时,转移为 f_{u,i}=\min_{j=0}^{{siz}_v} f_{u,i-j}+f_{v,j}+Sw

注意外层 i 逆序枚举以保证 01 背包的性质,内层枚举顺序无所谓,但是一定要在开始时处理 j=0,也就是直接把这个子树中纯白的情况考虑进去了,否则在后面会直接加上 Sw 导致算重。

C - 小 N 的独立集

考虑最朴素的算法,2^n 枚举每个点的取值,然后直接 O(n) 跑树上最大独立集,设 f_{u,0/1} 表示 u 不选/选时,u 子树内的最大收益。接着考虑 DP 套 DP,把 f 设入状态,设 g_{u,v_0,v_1} 为以 u 为根的子树中 u 不选时最大值为 v_0,选时最大值为 v_1 的方案数,更新时用 g_{v,p,q} 更新 g_{u,i+\max(p,q),j+p}=g_{u,i+\max(p,q),j+p}+g_{v,p,q}\times g_{u,i,j}

但是这样复杂度为 O(n^4k^4),无法通过,考虑把后两维修改压一下,变成 g_{u,v_0,v_1} 表示强制不选 u 时最大为 v_0,不强制时最大为 v_1。可以发现 v_1-v_0\le w_u\le k,所以改为 g_{u,v_0,d} 表示强制不选 u 时最大为 v_0,不强制时最大为 v_0+d,转移时 g_{u,i+p+q,\max(i+p+q,i+j+p)-(i+p+q)} 加上 g_{u,i,j}\times g_{v,p,q},时间复杂度 O(n^2k^4)

H - 船往低处流

Too Hard. 好像是各种换根分讨,感觉很麻烦。

斜率优化

D - 序列分割

首先注意到确定分割位置后,无论分割顺序怎样,最终的结果相同,因为每个元素都会与所有与其所在部分不同的元素相乘。所以设 f_{i,j} 表示考虑到前 i 个元素,分了 j 块的最大得分,有 f_{i,j}=\max_{k=j-1}^{i-1}f_{k,j-1}+s_k(s_i-s_k),其中 sa 的前缀和数组。

j 维滚动掉,拆开这个式子,得到 f_{i}=\max_{k=1}^{i-1}f_{k}+s_ks_i-s_k^2),即 -s_is_k+f_i=f_k-s_k^2,把 -s_i 看作 ks_k 看作 xf_i 看作 bf_k-s_k^2 看作 y,就是 kx+b=y 的形式。相当于拿着斜率为 -s_i 的直线在第一象限扫,扫到最高的经过某一点 (s_k,f_k-s_k^2) 时为最大截距 f_i

所以按照 (s_k,f_k-s_k^2) 维护一个上凸包,也就是斜率递减的折线。每次找到最后一条斜率大于目前 -s_i 的线段,最优决策点即为这条线段后面的端点,可以二分。但是由于 -s_i 递减,也就是所用直线的斜率递减,所以说决策点不会前移,用双端队列同时一直在队头弹出无用的点,每次的决策点就是队头了。

A - Yet Another Partiton Problem

Too Hard. 分治 + 斜率优化,还是太困难了。

决策单调性

F - Dispatch Money

Too Hard. 分治 + 决策单调性,还是太困难了。

其他 Trick

B - 经营与开发

发现每次相当于给答案加一个值 x_i 并使后面的答案乘一个值 y_i,若 type=1,则 x_i=a_iw,y_i=(1-0.01k),否则 x_i=-b_iw,y_i=(1+0.01c),发现按顺序枚举会有后效性,考虑倒序处理,每次相当于乘上 y_i 并加上 x_i,由于其他影响本处操作的操作在前面,所以后效性没了,求出最后答案乘上原始的 w 即可。

O - 管道取珠

发现 a_i^2 相当于操作两轮且序列相同的方案数,所以设 f_{i,j,k} 表示两轮都操作了 i 次,第一轮在上管取了 j 个,第二轮在上管取了 k 个,且操作序列相同的方案数。则转移有:

答案即为 f_{n+m,n,n}