AtCoder Educational DP Contest 做题记录

由于我的一个 dp 是在太差,于是就是在朋友的推荐下过来板刷这场了,之后 Typical DP Contest 也会有的(吧?)

A

题目链接:https://www.luogu.com.cn/problem/AT_dp_a

\(f_i\) 表示跳到第 \(i\) 个位置的最小代价,有:

\[f_i=\min(f_{i-2}+|h_i-h_{i-2}|,f_{i-1}+|h_i-h_{i-1}|) \]

https://atcoder.jp/contests/dp/submissions/70370463

B

题目链接:https://atcoder.jp/contests/dp/tasks/dp_b

依旧设 \(f_{i}\) 表示跳到第 \(i\) 个位置的最小代价,有:

\[f_i=\min_{j=1}^{\min(i-1,k)}(f_{i-j}+|h_i-h_{i-j}|) \]

https://atcoder.jp/contests/dp/submissions/70370677

C

题目链接:https://atcoder.jp/contests/dp/tasks/dp_c

为了方便书写,我们不妨设有 \(k\) 个活动(此处 \(k=3\),且从 \(0\) 开始标号),设 \(f_{i,j}\) 表示枚举到第 \(i\) 天,这天选了第 \(j\) 个活动,那么我们有:

\[f_{i,j}=\max_{p=0}^{k-1}[p\ne j]\times f_{i-1,p} \]

时间复杂度 \(O(nk^2)\)

https://atcoder.jp/contests/dp/submissions/70370863

D

题目链接:https://atcoder.jp/contests/dp/tasks/dp_d

\(f_{i,j}\) 表示枚举到第 \(i\) 个物品,已经用了 \(j\) 的容量,我们有:

\[f_{i,j}=\min(f_{i-1,j},f_{i-1,j-w_i}+v_i) \]

https://atcoder.jp/contests/dp/submissions/70371181

注意到第一维每次只会需要上一行,而且更新只会需要前面的东西,我们可以把第一维丢掉,第二维从后往前更新。

https://atcoder.jp/contests/dp/submissions/70371193

E

题目链接:https://atcoder.jp/contests/dp/tasks/dp_e

这次 \(W\) 变的很大,但是 \(v_i\) 很小啊!
所以我们考虑设 \(f_{i,j}\) 表示枚举到第 \(i\) 位,价值为 \(j\) 的最小重量,有:

\[f_{i,j}=\min(f_{i-1,j},f_{i-1,j-v_{i}}+w_i) \]

https://atcoder.jp/contests/dp/submissions/70371467

按照 D 的优化思路依旧可以把空间复杂度降下来。

https://atcoder.jp/contests/dp/submissions/70371480

F

题目链接:https://atcoder.jp/contests/dp/tasks/dp_f

\(f_{i,j}\) 表示 \(s\) 匹配到第 \(i\) 位,\(t\) 匹配到第 \(j\) 位长度的最大值,有:

\[f_{i,j}=\max(f_{i-1,j},f_{i,j-1},f_{i-1,j-1}+[s_i=s_j]) \]

题目要求输出方案,记录转移过来的地方就好了。

https://atcoder.jp/contests/dp/submissions/70372181

G

题目链接:https://atcoder.jp/contests/dp/tasks/dp_g

DAG 上 dp。设 \(f_u\) 表示到第 \(u\) 个点的最长路,那么有 \(f_{u}=max(f_{v}+1)\),其中 \(v\) 为指向 \(u\) 的点。那么做个拓扑排序顺便 dp 即可。

https://atcoder.jp/contests/dp/submissions/70372401

H

题目链接:https://atcoder.jp/contests/dp/tasks/dp_h

\(f_{i,j}\) 表示 \((1,1)\) 走到 \((i,j)\) 的情况数,则有:

\[f_{i,j}=f_{i-1,j}\times [c_{i-1,j}\ne\#]+f_{i,j-1}\times [c_{i,j-1}\ne\#] \]

https://atcoder.jp/contests/dp/submissions/70372613

I

题目链接:https://atcoder.jp/contests/dp/tasks/dp_i

\(f_{i,j}\) 表示枚举到第 \(i\) 个硬币,向上的次数有 \(j\) 次,则有:

\[f_{i,j}=f_{i-1,j-1}\times p_i+f_{i-1,j}\times (1-p_i) \]

https://atcoder.jp/contests/dp/submissions/70372815

J

开始上难度了(?

题目链接:https://atcoder.jp/contests/dp/tasks/dp_j

首先寿司 \(1/2/3\) 的先后顺序不影响,我们需要记录的东西只有第 \(i\) 个寿司还剩多少个。

\(f_{a,b,c,d}\) 表示还剩 \(0/1/2/3\) 个寿司的个数,我们有:

\[f_{a,b,c,d}=1+\frac anf_{a,b,c,d}+\frac bnf_{a+1,b-1,c,d}+\frac cnf_{a,b+1,c-1,d}+\frac dnf_{a,b,c+1,d-1} \]

可以这么理解,首先抽一次,然后选到第 \(0/1/2/3\) 个寿司的概率分别是 \(\frac{a}{n}\frac{b}{n}\frac{c}{n}\frac{d}{n}\)。所以转移一下就好。

我们发现式子前面后面都有 \(f_{a,b,c,d}\),貌似不会转移,移项得:

\[\frac{b+c+d}{n}f_{a,b,c,d}=1+\frac bnf_{a+1,b-1,c,d}+\frac cnf_{a,b+1,c-1,d}+\frac dnf_{a,b,c+1,d-1} \]

左边系数化为 \(1\),得:

\[f_{a,b,c,d}=\frac{n}{b+c+d}+\frac{b}{b+c+d}f_{a+1,b-1,c,d}+\frac{c}{b+c+d}f_{a,b+1,c-1,d}+\frac{d}{b+c+d}f_{a,b,c+1,d-1} \]

我们发现状态数过多了,想想能省略什么。发现只要知道 \(1/2/3\) 的个数就能算出 \(0\) 的个数,所以把第一维优化掉即可:

\[f_{b,c,d}=\frac{n}{b+c+d}+\frac{b}{b+c+d}f_{b-1,c,d}+\frac{c}{b+c+d}f_{b+1,c-1,d}+\frac{d}{b+c+d}f_{b,c+1,d-1} \]

时间复杂度 \(O(n^3)\)

https://atcoder.jp/contests/dp/submissions/70373723

K

题目链接:https://atcoder.jp/contests/dp/tasks/dp_k

涉及到博弈的 dp。我们知道这个题里面没有平局,所以每个状态一定有人必胜,所以我只要有一步操作,使得对方必败,那么我这个状态就是必胜的。

我们不妨设 \(f_{0/1,j}\) 表示先手/后手剩 \(j\) 个石子没取为必胜/必败态,有:

\[f_{op,j}=\operatorname{or}_{i}^{a_i\le j} [f_{op^1,j-a_i}=0] \]

答案即为 \(f_{0,k}\)

https://atcoder.jp/contests/dp/submissions/70378358

L

题目链接:https://atcoder.jp/contests/dp/tasks/dp_l

区间 dp。令 \(f_{i,j}\) 表示区间 \([i,j]\) 的答案,那么转移显然有,先手操作时:

\[f_{i,j}=\max(f_{i+1,j}+a_i,f_{i,j-1}+a_j) \]

后手操作时:

\[f_{i,j}=\min(f_{i+1,j}-a_i,f_{i,j-1}-a_j) \]

答案是 \(f_{1,n}\)

https://atcoder.jp/contests/dp/submissions/70378598

M

题目链接:https://atcoder.jp/contests/dp/tasks/dp_m

\(f_{i,j}\) 表示第 \(i\) 个位置放 \(j\) 个的方案数,显然有 \(f_{0,0}=1\),我们可以写出:

\[f_{i,j}=\sum_{k=0}^{\min(a_i,j)}f_{i-1,j-k} \]

此时时间复杂度 \(O(nk^2)\),考虑优化。
我们发现 \(\sum_{k=0}^{\min(a_i,j)}f_{i-1,j-k}\) 不用遍历,只需要做一个前缀和计算就好了,令 \(sum_{i,j}=\sum_{k=0}^{j}f_{i,k} = sum_{i,j-1}+f_{i,j}\),然后计算即可。

https://atcoder.jp/contests/dp/submissions/70379042

N

题目链接:https://atcoder.jp/contests/dp/tasks/dp_n

\(sum_{i}\) 表示 \(\sum_{j=1}^{i}a_j\) \(f_{i,j}\) 表示区间 \([i,j]\) 的答案,那么我们有:

\[f_{i,j}=\min_{k=i}^{j-1}(f_{i,k}+f_{k+1,j}+sum_j-sum_{i-1}) \]

表示枚举断点,然后计算左右区间已经合并的答案,和这次合并要的答案。
注意刚开始 \(f_{i,i}=0\),因为没开始合并。

https://atcoder.jp/contests/dp/submissions/70379464

O

题目链接:https://atcoder.jp/contests/dp/tasks/dp_o

\(f_{i,S}\) 表示当前枚举到第 \(i\) 个男生,女生选择的状态为 \(S\),有:

\[f_{i,S}=f_{i-1,S-2^j}(S\operatorname{and}2^j=1) \]

所以说,我们把每个 \(S\)\(1\) 的位置找出来,总共只有 \(O(n2^n)\) 个位置,然后 dp 只有 \(O(n2^n)\) 个状态,转移 \(O(n2^n)\) 次,时间复杂度 \(O(n2^n)\),注意取模不要太多。

https://atcoder.jp/contests/dp/submissions/70379741

P

题目链接:https://atcoder.jp/contests/dp/tasks/dp_p

树形dp,设 \(f_{0/1,u}\) 表示点 \(u\) 的颜色为黑/白时的答案。那么有:

\[f_{0,u}=\prod_{v\in u} f_{1,v} \]

\[f_{1,u}=\prod_{v\in u} f_{0,v}+f_{1,v} \]

\(v\in u\) 表示 \(v\)\(u\) 的子节点。

https://atcoder.jp/contests/dp/submissions/70379920

Q

题目链接:https://atcoder.jp/contests/dp/tasks/dp_q

\(f_i\) 表示以 \(i\) 结尾的答案,那么有:

\[f_i=\max_j^{h_j<h_i}f_{j}+a_i \]

注意到这个玩意要 \(O(n^2)\),考虑优化。
我们发现我们要查的是所有 \(h_j<h_i\)\(j\)\(f_j\) 的最大值,我们建一颗值域树状数组,求前缀 \(\max\) 即可,时间复杂度 \(O(n\log n)\)

https://atcoder.jp/contests/dp/submissions/70380014

R

题目链接:https://atcoder.jp/contests/dp/tasks/dp_r

想到 floyd,令 \(f_{x,i,j}\) 表示跑了 \(x\) 步之后的 \(i\)\(j\) 的方案数,我们可以递推出:

\[f_{x,i,j}=\sum_{k=1}^{n} f_{x-1,i,k} \times f_{1,k,j} \]

所以跑了 \(k\) 步等价于 \({f_{1}}^k\),矩阵快速幂解决即可。

S

题目链接:https://atcoder.jp/contests/dp/submissions/70382692

数位 dp 板子。

\(f_{idx,0/1,res}\) 表示当前到第 \(idx\) 位,是否有前导 \(0\),当前数位和 \(\bmod d\)
\(mx\) 为当前位置可以枚举到的最大的数字,那么我们有:

\[f_{idx,zr,res}=\sum_{i=0}^{mx} f_{idx+1,zr \operatorname{and} [i=mx],(res+i)\bmod d} \]

https://atcoder.jp/contests/dp/submissions/70382692

T

题目链接:https://atcoder.jp/contests/dp/tasks/dp_t

神秘的 dp 设计。令 \(f_{i,j}\) 表示长度为 \(i\),填写 \([1,i]\),以 \(j\) 结尾的方案数,对于新加入一个 \(j\),我们可以把原本 \(\ge j\) 的部分全部 \(+1\),这样就是一个排列了。

那么我们有:

\(f_{i,j}=\left\{\begin{matrix} \sum_{k=1}^{j-1} f_{i-1,k} & s_{i-1}=\texttt{"<"}\\ \sum_{k=j}^{i-1} f_{i-1,k} & s_{i-1}=\texttt{">"} \end{matrix}\right.\)

注意到后面是一段区间的和,用前缀和优化即可。

https://atcoder.jp/contests/dp/submissions/70384107

U

题目链接:https://atcoder.jp/contests/dp/tasks/dp_u

首先我们考虑求出每个选择方式的权值,这里复杂度 \(O(n^22^n)\),然后做一个类似背包的东西,使用枚举子集可以做到这部分 \(O(3^n)\)

https://atcoder.jp/contests/dp/submissions/70386668

V

题目链接:https://atcoder.jp/contests/dp/tasks/dp_v

\(f_{u}\) 为以 \(1\) 为根时以 \(u\) 为根的子树中的答案,那么有:

\[f_{u}=\prod_{v\in u} (f_{v}+1) \]

那么对于 \(g_u\) 表示以 \(u\) 为根的子树外的答案,那么有:

\[g_{v}=g_{fa_v}\times \frac{f_{fa_{v}}}{f_{v}+1} \]

可是 \(m\) 不一定为质数,有可能没有逆元,所以不妨将式子转换一下,把 \(f_{fa_u}\) 拆开来,变成 \(\prod_{v\in u} (f_{v}+1)\) 的形式,处理前缀积后缀积计算即可。

https://atcoder.jp/contests/dp/submissions/70484050

W

题目链接:https://atcoder.jp/contests/dp/tasks/dp_w

\(f_{i,j}\) 表示当前为第 \(i\) 位,上一个填 \(1\) 的位置为 \(j\) 的最大值。首先我们有 \(f_{i,i}=\max_{j=0}^{i-1}f_{i,j}\)\(f_{i,j}=f_{i-1,j}\)。然后我们考虑线段的影响,对于一段线段 \([l_x,r_x]\),我们按照右端点排序,枚举到右端点的时候,我们有 \(f_{i,j}=f_{i-1,j}+\sum_{r_x=i,l_x\le j}a_x\),即加上所有右端点为 \(i\) 且覆盖 \(j\) 的线段进去。

我们发现后面这一大坨可以用线段树优化转移,那么时间复杂度 \(O(n\log n)\)

https://atcoder.jp/contests/dp/submissions/70492058

X

题目链接:https://atcoder.jp/contests/dp/tasks/dp_x

因为箱子的摆放顺序会影响 dp 答案,我们不妨贪心的去考虑,箱子 \(x,y\),如果 \(x\) 摆在下面,那么有 \(s_x-w_y>s_y-w_x\),即 \(s_x+w_x>s_y+w_y\),所以我们按照 \(w+s\) 从小到大排序之后做一个背包就好了。

https://atcoder.jp/contests/dp/submissions/70492919

Y

题目链接:https://atcoder.jp/contests/dp/tasks/dp_y

快结束了捏。

考虑容斥,首先对于 \((x_1,y_1)\) 走到 \((x_2,y_2)\),答案是 \(\binom{|x_2-x_1|+|y_2-y_1|}{|x_2-x_1|}\)。我们令 \(f_{i}\) 表示从 \((1,1)\) 到第 \((x_i,y_i)\) 不经过障碍的情况数,那么我们有:\(f_i=\binom{x_i+y_i-2}{x_i-1}-\sum_{j=1}^{i-1}f_j\times \binom{|x_i-x_j|+|y_i-y_j|}{|x_i-x_j|}\)

令第 \(n+1\) 个点为 \((h,w)\),答案即是 \(f_{n+1}\)

https://atcoder.jp/contests/dp/submissions/70493932

Z

题目链接:https://atcoder.jp/contests/dp/tasks/dp_z

首先显而易见的可以令 \(f_i\) 为到达 \(i\) 的答案,那么显然有:

\[f_i=\max_{j=1}^{i-1} f_j+(h_i-h_j)^2+C \]

时间复杂度 \(O(n^2)\),过不了啊/ll

我们考虑拆开来式子,变成:

\[f_j+{h_i}^2-2h_ih_j+{h_j}^2+C \]

这个东西我们发现,\({h_i}^2\)\(C\) 都是一定会加上来的,最后考虑。

然后我们发现,\(f_j\)\({h_j}^2\) 都是根据 \(j\) 就可以求出来的,不妨设他为 \(calc(j)=f_j+{h_j}^2\),同时我们把 \(-2h_i\) 看成 \(k\),那么转移式子就变成了一个 \(calc(j)+kh_j\) 的形式,形似一次函数,用李超线段树暴力做即可

当然我们是为了学习斜率优化 dp 的,所以考虑再推推式子。

我们考虑 \(x<y\)\(y\) 不比 \(x\) 优时是什么情况,那么有 \(calc(x)+kh_x\le calc(y)+kh_y\)

移项得:\(calc(x)-calc(y)\le k(h_y-h_x)\),即 \(\frac{calc(x)-calc(y)}{h_y-h_x}\le k\)

\(k=-2h_i\) 带入,得:\(\frac{calc(x)-calc(y)}{h_y-h_x}\le -2h_i\),即 \(\frac{calc(x)-calc(y)}{h_x-h_y}\ge 2h_i\)。因为这个玩意神似斜率,我们称之为斜率优化。

我们考虑维护这个斜率 \(k\),假设当前有三个点 \(a,b,c\),有 \(k(a,b)\ge k(b,c)\),那么显然 \(b\) 在直线 \(ac\) 上方,选择 \(b\) 是更裂的。我们用单调队列维护这个东西。由于 \(h\) 单调递增,我们不需要在凸壳上二分,只需要每次弹出最前面的直到满足条件就好啦,时间复杂度 \(O(n)\)

https://atcoder.jp/contests/dp/submissions/70494814

posted @ 2025-10-23 20:00  coding_goat_qwq  阅读(19)  评论(0)    收藏  举报