「赛前备战」NOIp2020-提高 动态规划训练

博主太菜,可能会炸联赛,于是恶补一下 QAQ

题目比较基础,动态更新

Tags

仅包含 提高组 内容。

  • 类型:

区间 dp背包 dp树形 dp状压 dp计数 dp数位 dp概率/期望 dp环形 dp基环树 dp

  • 优化:

单调栈/单调队列 优化其他数据结构 优化斜率 优化倍增 优化

Summarize

简单总结了部分基础类型的 dp 以及一些优化。

区间 dp

基础状态: (f(l, r)) 表示区间 ([l, r]) ……

基础转移模型示例:(f(l,r) = minlimits_{k}{f(l, k) + f(k+1, r)+cdots})

基本实现:枚举区间长度,枚举左端点并计算出右端点,枚举断点。或者直接记搜。

树形 dp

树上动态规划。

基础状态:(f(x, cdots)) 表示以 (x) 为根的子树……

由于树的递归性质,基本用 Dfs 实现。

状压 dp

常见于元素个数较少的情况。

基础状态:(f(S, cdots)) 表示以 (x) 为根的子树……

集合使用二进制、位运算的思想压缩成一个整数并进行判断或转移。

计数 dp

常用于统计方案数。

设计状态是重点,转移看题意。

可能有一些组合计数的知识。

数位 dp

常见情况:统计值域在 ([a, b]) 中满足……条件的数的个数/数位数字和/……((a, b) 通常很大)

一般拆成 ( ext{ans}(b) - ext{ans}(a - 1)) 计算,以记忆化搜索实现。

搜索函数的参数一般有当前位置,前面是否有前导零,枚举时是否受当前位大小限制。但大多数题目还会有其他限制。

数位 dp 的复杂度和位数有关。

概率/期望 dp

一般情况下,解决概率问题需要顺序循环,而解决期望问题使用逆序循环,如果定义的状态转移方程存在后效性问题,还需要用到转化成求解方程组的解并使用高斯消元。

部分题目还会牵扯到状压、矩阵加速等。

对分数取模记得逆元。

环形 dp

环上的 dp,一般的处理方法有断环为链然后 dp 两次或 (n) 次,或者复制一遍。

基环树 dp

环形 dp + 树形 dp。据说很烦,不会。

背包 dp

主要是挖出经典模型。

单调栈/单调队列 优化

一般会出现滑动最值差不多的模型,如:(f(i) = minlimits_{j<i}{f(j)+ ext{val}(i, j) }),其中 ( ext{val}(i, j)) 为一个仅存在关于 (i) 或关于 (j) 的项的多项式,不存在同时关于 (i, j) 的项。

此时可以提出关于 (i) 的项,用单调队列维护 (f(j)) 和关于 (j) 的项,加速转移。

斜率 优化

上述 单调栈/单调队列 优化 的拓展。适用于 ( ext{val}(i, j)) 中含有同时关于 (i, j) 的项时的情况。

一般可以假设来两个决策点 (a, b(a<b<i)),并设 (a) 劣于 (b)。我们可以得到一个不等式,然后变形为 (dfrac{y_a - y_b}{x_a - x_b} = ext{slope}(a, b)) 的形式。最后用单调队列维护上/下凸包即可。

其他数据结构 优化

一般根据转移的特征,使用合适的数据结构优化转移。

倍增 优化

也许会有类似与 (2^k) 这种明显的提示。

一般把“转移 (j) 次”换成“转移 (2^k) 次”降下复杂度。

Content

  • 「eJOI2017」Experience 树形 dp
  • 「eJOI2018」山
  • 「CSP2019-J」纪念品 背包 dp
  • 「NOIp2018-普及」摆渡车
  • 「AHOI2009」中国象棋 计数 dp
  • 「LA 4394」String painter 区间 dp
  • 「CSP2019-S」Emiya 家今天的饭
  • 「Codeforces」Coloring Brackets 区间 dp
  • 「USACO19DEC」Greedy Pie Eaters 区间 dp
  • 「NOIp2018-提高」愤怒的小鸟 状压 dp
  • 「Codeforces 372C」Watching Fireworks is Fun 单调栈/单调队列 优化
  • 「Codeforces 16E」Fish 状压 dp 概率/期望 dp
  • 「POI2014」Little Bird 单调栈/单调队列 优化
  • 「POJ 1746」Minimizing maximizer 其他数据结构 优化 - 线段树
  • 「POJ 2836」Rectangular Covering 状压 dp
  • 「POJ 1821」Fence 单调栈/单调队列 优化
  • 「HDU 3507」Print Article 斜率 优化
  • 「HNOI2008」玩具装箱 斜率 优化
  • 「HDU 2829」Lawrence 斜率 优化
  • 「THUSC 2016」成绩单 区间 dp
  • 「Codeforces 148D」Bag of mice 概率/期望 dp
  • 「Luogu P1654」OSU! 概率/期望 dp
  • 「Luogu P3413」SAC#1 - 萌数 数位 dp
  • 「SDOI2016」征途 斜率 优化
  • 「Luogu P1613」跑路 倍增 优化

「eJOI2017」Experience

update - 2020.7.27

(f(x, 0), f(x, 1)) 分别表示以 (x) 为根的子树,(x) 所在链为向下(非严格)递减、递增时的答案。

题目中要求求极差,那么结点 (x) 可能为最大值或最小值。

以向下递减为例,我们在所以满足 (W_x ge W_y) 的所有子结点 (y) 中,将原本 (y) 的贡献用 (x) 作为新的最大值替换,并更新 (f(x, 0)) 的答案。向下递增也是同理,不过这里是作为最小值,因此符号会改变。

在做结点 (x) 是,我们设 (p = sumlimits_{y in ext{son}(x)} max(f(y, 0), f(y, 1))),并令 (p)(f(x, 0), f(x, 1)) 的初始值。

那么状态转移方程((leftarrow) 表示最(大)值操作):

[f(x, 0) leftarrow maxlimits_{yin ext{son}(x), W_x ge W_y}{p - max(f(y, 0), f(y, 1)) + W_x - W_y}\ f(x, 1) leftarrow maxlimits_{yin ext{son}(x), W_x le W_y}{p - max(f(y, 0), f(y, 1)) - W_x + W_y} ]

时间复杂度 (O(n))

「eJOI2018」山

update - 2020.7.27

(f(n, k, 0), f(n, k, 1), f(n, k, 2)) 表示 ([1, n]) 中,建造了 (k) 座房子,且第 (n-1),第 (n) 个位置的状态分别为 ((0,0),(1, 0), (0, 1)) 时((0) 表示没有房子,(1) 表示有)的最小花费时间。

状态转移方程:

[f(n, k, 0) = min{f(n-1, k, 0), f(n-1, k, 1)} \ f(n, k, 1) = f(n-1, k, 2) + ext{get}(a_n - a_{n - 1} + 1)\ f(n, k, 2) = min{f(n-1, k-1, 0) + ext{get}(a_{n-1} - a_n + 1), f(n-1, k-1,1)+ ext{get}(min(a_{n-2}-1, a_{n-1})-a_{i}+1)}\ ext{其中 } ext{get}(x)=max(x, 0) ]

时间复杂度 (O(n^2))

「CSP2019-J」纪念品

update - 2020.7.27

直接存物品的个数显然原地升天。但仔细想一下发现,转换成统一在第 (i) 天买,然后在第 (i+1) 天卖,其实等价。因为即使对某种物品实际上并不操作,换成全卖了然后买回来也未尝不可。

不难设计出一个比较暴力的状态:(f(i, j, k)) 表示第 (i) 天,对于前 (j) 个物品,剩余现金为 (k) 时,下一天手头上的最大现金数。转移方程显然。

为了不空间爆炸,我们尝试优化。第一维显然可以缩掉——我们只要上一轮的最优值即可。剩下了两维看着像一个背包,于是倒序以下就只剩最后一维了。方程:

[f(k) leftarrow max(f(k), f(k - P_{i, k})+P_{i, k+1}-P_{i, k}) ]

时间复杂度 (O(TN imes max P))

「NOIp2018-普及」摆渡车

update - 2020.7.28

(t) 数组升序排序,显然的。设 (f(i, j)) 表示第 (i) 个人等了 (j) 分钟,送走前 (i) 个人等待总时间的最小值。

直接看 (j) 的范围貌似可以达到 (max t),但其实不然——一个人最多等 (< 2m) 分钟。

  • 相比在起点停着 (m) 分钟,啥也不管直接开一趟(尽管空车)总不会更劣,那么停车一次不超过 (T_1 < m)
  • 一个人等一辆离开的车回来的时间 (T_2 le m)

于是一个人等待的时间 (j = T_1 + T_2 < 2m)

那么状态的规模差不多是 (O(n m))。考虑设计转移:

  • (t_i + j ge t_{i + 1}),那么第 (i+1) 个人赶得上本趟车,则:(f(i+1, j+t_i-t_{i+1}) leftarrow f(i,j)+j+t_i-t_{i+1})
  • (t_i+j+mge t_{i+1}),那么第 (i+1) 个人可以在车回来前等车,则枚举车回来的时间 (k),转移为: (f(i+1, j+t_i+m+k-t_{i+1}) leftarrow f(i, j)+j+t_i+m+k-t_{i+1})
  • (t_i + j + m < t_{i +1}),那么第 (i+1) 个人在车回到起点后到,则枚举第 (i+1) 个人的等待时间 (k),转移为: (f(i+1, k) leftarrow f(i, j)+k)

时间复杂度:(O(nm^2))

「AHOI2009」中国象棋

update - 2020.7.28

会设状态基本就做完了。设 (f(i, j, k)) 表示考虑到第 (i) 行,有 (j) 列存在一个棋子,(k) 列有两个棋子的方案数。

转移非常显然:

  • 不放:(f(i, j, k) leftarrow f(i-1, j, k))
  • 放在空列:
    • 放一个:(f(i, j, k) leftarrow f(i - 1, j - 1, k) imes (m - (j - 1) - k))
    • 放两个:(f(i, j, k) leftarrow f(i - 1, j - 2, k) imes C_{m - (j - 2) - k}^2)
  • 放在已经有一个的列:
    • 放一个:(f(i, j, k) leftarrow f(i-1, j+1, k-1) imes (j+1))
    • 放两个:(f(i, j, k) leftarrow f(i - 1, j + 2, k - 2) imes C_{j+2}^2)
  • 在已有一个的和空列各放一个:(f(i, j, k) leftarrow f(i - 1, j, k - 1) imes j imes (m - j - (k - 1)))

时间复杂度 (O(nm^2))

「LA 4394」String painter

update - 2020.7.28

直接算 (A, B) 串不好处理,不如先考虑空串到 (B) 的步数。设 (f(i, j)) 表示从空串到 (B[i,j]) 的最小步数。

那么显然有:

[f(i,j) = egin{cases} 0 & i>j\ 1 & i=j\ minlimits_{i < k le j, B_i = B_k} {f(i+1, j)+f(k + 1, j)} & i < j\ end{cases} ]

我们令答案为 (g)(g(i)) 表示 ([1, i]) 部分的答案。那么:

[g(i) leftarrowminlimits_{0le j < i} {g(j) + f(j + 1, i)}\ g(i) leftarrow min(g(i), g(i - 1) imes [A_i = B_i]) ]

答案为 (g(n)),复杂度 (O(n^3))

「CSP2019-S」Emiya 家今天的饭

update - 2020.7.28

分析题目性质,发现不可能有两列或以上占去一半,不合法的只可能是一列。

那么容斥以下,答案等于 总方案数 减去 某一列不合法的方案数。

合法的方案不难处理:(g(i, j)) 表示前 (i) 行选了 (j) 个的方案数,那么显然 (g(i, j) = g(i - 1, j) + g(i - 1, j - 1) imes sum_j a_{i,j})。可以在 (O(n^2)) 时间内解决。

对于不合法的方案,有一个比较暴力的思路,枚举这个不合法列 (c),设 (f(i, j, k)) 为前 (i) 行,在第 (c) 列选了 (j) 个,其他列选了 (k) 个的方案数。那么有转移:

[f(i, j, k) = f(i - 1, j, k) + f(i - 1, j - 1, k) imes a_{i, c} + (sum_j a_{i, j} - a_{i, k}) imes f(i - 1, j, k - 1) ]

不合法的总数即为所有 (sum_{j>k} f(n, j, k)) 之和。然而这个需要 (O(n^3 m)) 的时间,只有 84 分。

突然发现我们只需要 (j, k) 之间的差值大小,并不需要具体大小,于是又砍掉一维—— (f(i, j)) 表示当不合法列为 (c) 时,考虑到前 (i) 行,第 (c) 列的个数比其他列多 (j) 个的方案数。转移方程:

[f(i, j) = f(i - 1, j) + f(i - 1, j - 1) imes a_{i, c} + f(i - 1, j + 1) imes (sum_j a_{i, j} - a_{i, c})。 ]

这样时间复杂度 (O(n^2m))

「Codeforces 149D」Coloring Brackets

update - 2020.7.29

显然不能直接用 (f(l, r)) 表示区间 ([l, r]) 的方案数,因为相邻位置也有影响。

于是带上 (l, r) 的颜色:(f(l, r, x, y)) 表示区间 ([l, r]) 中,位置 (l) 颜色为 (x)(r)(y) 的方案数。颜色 (0/1/2) 分别表示无色,蓝色,红色。

那么分三种情况讨论:

  • (l + 1 = r):边界条件,(f(l, r, 0, 1) = f(l, r, 1, 0) = f(l, r, 0, 2) = f(l, r, 2, 0) = 1)
  • 位置 (l, r) 为一组配对的括号:我们可以由区间 ([l + 1, r - 1]) 的 dp 值推出,转移显然,根据题意模拟即可。
  • 位置 (l, r) 为不配对:那么我们可以由两组配对的 dp 值推出。具体地,设位置 (l) 的右括号的位置为 (k),那么答案就由区间 ([l, k])([k + 1, r]) 得出。

推荐使用记忆化搜索实现,复杂度 (O(|s|^2))

「USACO19DEC」Greedy Pie Eaters

update - 2020.7.29

(f(i, j)) 表示区间 ([i, j]) 被全部吃完后可得的最大 (sum w_i)。那么有

[f(i, j)=max egin{cases} maxlimits_{i le k <j} f(i, k) + f(k, j) \ maxlimits_{ile kle j} f(i, k - 1)+f(k + 1, j) +p(i, j, k) end{cases} ]

其中 (p(i, j, k)) 表示所有满足 (i le l_xle kle r_xle j)(k)(w_k) 的最大值。求 (p) 数组可以用向外拓展的方式:

[p(i-1, j, k) leftarrow p(i, j, k)\ p(i, j+1, k) leftarrow p(i, j, k) ]

在计算 (p) 时应先枚举 (k)。时间复杂度 (O(n^3))

「NOIp2018-提高」愤怒的小鸟

update - 2020.7.29

众所周知三点确定一条抛物线。那么设 (s(i, j)) 表示点 ((x_i, y_i), (x_j, y_j), (0, 0)) 构成的抛物线上的点集。显然可以 (O(n^3)) 预处理。

设计状态:(f(S)) 表示覆盖点集 (S) 所需的最小抛物线数。转移:

[f(varnothing) = 0 \ f(S cup s(i, j)) = min{f(S) + 1} \ f(S cup (x_i, y_i)) = min{f(S) + 1} ]

这个方法是 (O(T imes 2^ncdot n^2)) 的,非常悬。

考虑一个优化:设 (x) 为点集 (S) 中没有出现过的最小编号的点的编号,即 (x = min{x| exttt{S&(1<<(x-1))}=0, xin mathbb N^+})。那么可以限定由 (S) 拓展的转移的线必定经过第 (x) 个点。

这相当于限定了顺序,因为原本乱序枚举与现在可以枚举到的范围其实是一样的。时间复杂度 (O(T imes 2^ncdot n))

「Codeforces 372C」Watching Fireworks is Fun

update - 2020.7.29

(f(i, j)) 表示前 (i) 个烟花放完后,当前在位置 (j) 处,得到的最大快乐值。那么有:

[f(i, j) = maxlimits_{|k-j|le d imes|t_i - t_{i-1}|}{f(i - 1, k) + b_i - |a_i - j|} ]

显然这个 dp 是 (O(n^2m)) 的,需要优化。

发现这个 (b) 可以提出来,那么重新定义 (f)

[f(i, j) = minlimits_{|k-j|le d imes|t_i - t_{i-1}|}{f(i - 1, k) - |a_i - j|} ]

答案即为 (sum b_i - min f(m, j))。又发现可以把 (|a_i-j|) 提出来,于是我们发现一个类似于 (f(i)) 一段滑动区间的最值问题,显然可以单调队列优化。发现 (k) 的范围其实有两段,于是正反各做一次, 时间复杂度 (O(nm))

直接开数组炸空间,于是滚动数组滚掉第一维。

「Codeforces 16E」Fish

update - 2020.7.30

首先要知道 (x) 个鱼中任意一对相遇的机率 (P(x) = frac{1}{x(x-1)/2})

(n) 很小,考虑状压——(f(S)) 表示存活下来的鱼的集合为 (S) 的概率。转移:

[f(S) = sumlimits_{i otin S,jin S} f(Scup i) imes a_{j, i} imes frac{1}{|S| imes(|S| + 1)/2} ]

后面的 (-1) 改成了 (+1) 是因为原本的集合大小为 (|S| + 1)

时间复杂度 (O(2^n imes n^2))

「POI2014」Little Bird

update - 2020.7.30

(f(i)) 为到达第 (i) 个位置的最小消耗。那么有:

[f(i) = minlimits_{j > 0, i - j le k} {f(j) + [d_i ge d_j]} ]

时间复杂度 (O(qnsum k_i)),直接爆炸。

考虑它是一个滑动最值,那么用单调队列优化这个过程。这里的比较关键字有两个:第一为 (f) 的大小,第二为 (d) 的大小。

优化后复杂度为 (O(qn)),STL 的话可能有点卡常。

「POJ 1746」Minimizing maximizer

update - 2020.7.30

有一个比较朴素的想法:(f(i, j)) 表示前 (i) 个 Sorter 中,位置 (j) 到位置 (1) 所需 Sorter 数量的最小值。那么有一个非常显然的转移:

[f(i, j) = egin{cases} min(f(i-1, j),minlimits_{jin [s_{i-1}, t_{i-1}]} {f(i-1, k)}) & j=t_{i-1}\ f(i-1, j) & j e t_{i-1}\ end{cases} ]

(O(nm)) 的时空复杂度根本无承受。我们发现第一位明显可以缩掉:

[f(t_i) leftarrow min(f(t_i), minlimits_{kin[s_i, t_i]}{f(k)} + 1) ]

然而最坏仍然是 (O(nm)) 的,于是使用线段树优化区间最值的过程,得到了 (O(mlog n)) 的优秀算法。

「POJ 2836」Rectangular Covering

update - 2020.7.30

首先预处理以所有点对为对顶点的矩形的面积和覆盖点集,分别即为 (s(i, j), p(i, j))

(f(S)) 为覆盖点集 (S) 所需的最小面积。状态转移方程:

[f(Scup p(i, j)) = minlimits_{i e j}(f(Scup p(i, j)), f(S) + s(i, j)) ]

注意共线的特判处理。时间复杂度 (O(2^n imes n^2))

「POJ 1821」Fence

update - 2020.7.31

先将工人按位置 (S) 排序。

(f(i, j)) 表示前 (i) 个工人,处理前 (j) 块木板,可得的 (sum P_i) 的最大值。朴素的状态转移:

[f(i, j) = max egin{cases} f(i, j-1) \ f(i-1, j) \ maxlimits_{j - L_ile kle S_i - 1} {f(i-1, k) + P_i imes (j-k)} end{cases} ]

显然第三个转移是 (O(n)) 的,考虑优化。

我们把第三个拆一下:(f(i, j) = maxlimits_{j - L_ile kle S_i - 1} {f(i-1, k) - P_i imes k}+P_i imes j)

发现中间是一个上界固定为 (S_i - 1),下界逐渐上升的区间最值问题。于是单调队列优化。

时间复杂度 (O(nm))

「HDU 3507」Print Article

update - 2020.7.31

第一道斜率优化 /cy

若令 (f(i)) 表示前 (i) 个的最小花费。那么有一个显然的转移:

[f(i) = minlimits_{k<i} {f(k)+(s(i) - s(k))^2+m}\ ext{其中}s(i)=sum_{j=1}^i C_i ]

(O(n^2)) 的时耗承受不了,考虑优化。

(k<j<i),若决策 (j) 优于 (k),那么满足不等式

[f(j)+(s(i)-s(j))^2+m<f(k)+(s(i)-s(k))^2+m ]

化简得

[2s(i)< dfrac{[f(k)+s^2(k)]-[f(j)-s^2(j)]}{s(k)-s(j)} ]

我们将 (j, k) 抽象成一个二维点 ((x_j, y_j), (x_k, y_k)),那么设 (x_t = s(t),y_t = f(t)-s^2(t)),于是有:

[2s(i)< dfrac{y_k - y_j}{x_k - y_k} = ext{slope}(k, j) ]

我们使用斜率优化,用单调队列维护这些点,斜率递增。

当做到第 (i) 个时,先弹出队首不符合上述不等式的元素,再将队首直接转移至 (f(i)),然后将队尾违背斜率单调性的弹出,最后在尾部插入 (i)。弹出时须保证队中存在两个及以上的元素。

时间复杂度 (O(n))

「HNOI2008」玩具装箱

update - 2020.7.31

设前 (i) 个的最小花费为 (f(i)),则:

[f(i) = minlimits_{j < i}{ f(j) + (i-k-1+s(i)+s(j)-L)^2 } \ ext{其中} s(i) = sum_{j=1}^i C_i ]

为简化计算,设 (a_t = t + s(t),b_t=t+s(t)+L+1),于是:

[f(i) = minlimits_{j<i} { f(j) + a_i^2+b_j^2-2a_i b_j} ]

对于某个 (j),我们将原转移化为:

[2a_i b_j + f(i) - a_i^2 = f(j) + b_j^2 ]

(x_t = b_t, y_t = f(t) + b_t^2),那么这可以被视为一个直线的表达式,为斜率即为 (2a_i)

斜率优化,维护一个斜率递增的单调队列即可,第一个满足斜率 ( ext{slope}(j, j+1) >2a_i)(j) 直接转移。

时间复杂度 (O(n))

「HDU 2829」Lawrence

update - 2020.8.1

显然的状态:(f(i, j)) 表示前 (i) 个站点,炸 (j) 次产生的最小值。

显然的方程:(f(i, j) = minlimits_{k<i} { f(k, j - 1) + c(i)-c(k)-s(k) imes (s(i)-s(k)) }),其中 (s(i) = sum_{j=1}^i a_i, c(i)) 表示前 (i) 个的两两乘积和,(=c(i-1)+a(i)s(i-1))

单次转移 (O(n)),需要优化。设 (a<b),且决策 (a) 劣于 (b),那么有 (f(a, j-1)-c(a)-s(a)s(i)+s^2(a) > f(b, j-1)-c(b)-s(b)s(i)+s^2(b)),即:

[dfrac{[f(a, j-1)-c(a)+s^2(a)] - [f(b, j-1)-c(b)+s^2(b)]}{s(a)-s(b)} > s(i) ]

(x_i = s(i), y_i = f(i, j-1)-c(i)+s^2(i)),那么带入上式:

[dfrac{y_a - y_b}{x_a-x_b}= ext{slope}(a, b) > s(i) ]

于是斜率优化。时间复杂度 (O(nm))

「THUSC 2016」成绩单

update - 2020.8.1

考虑区间动规,但显然不是 (f(i, j)),因为代价与最值有关,而我们这样是无法知道的。

正确的设法应该是,(f(i, j, l, r)) 表示区间 ([i, j]) 消去部分数,剩下数在值域 ([l, r]) 中的最小花费。

转移?分类讨论:

  • (l = r = 0)。说明我们需要取完,分为直接取完整个区间,或先取这个区间中某段值域的数字:

[f(i, j, l, r) = min(a+b imes (maxlimits_{ile kle j} w_i - minlimits_{ile kle j} w_i)^2, minlimits_{x, y} {f(i, j,x, y)}+a+b imes (y-x)^2) ]

  • (l e 0)(r e 0)。考虑分割这个区间。
    • 当然如果 (i=j) 的话只要判断当前值 (w_i) 是否 (in [l, r]) 即可。
    • 否则枚举断点 (k),左右两部分共有 3 中情况:左边取完,右边取完,都不取完:

[f(i, j,l, r)leftarrow f(i, k, l, r) + f(k+1, j, l, r) \ f(i, j,l, r)leftarrow f(i, k, 0, 0) + f(k+1, j, l, r) \ f(i, j,l, r)leftarrow f(i, k, l, r) + f(k+1, j, 0, 0) ]

可以使用记忆化搜索实现,总复杂度 (O(n^5)),但由于状态实际并没有这么多,跑不满。

「Codeforces 148D」Bag of mice

update - 2020.8.3

(f(i, j)) 表示轮到公主时,袋中剩下 (i) 只白,(j) 只黑,公主胜的概率。

首先 (f(i, j) leftarrow frac{i}{i + j}),即一下就抓到白的的情况。

两人都抓到黑的,中途跑出一只黑的:

[f(i, j) leftarrow f(i, j - 3) imes frac{j}{i +j} imesfrac{j-1}{i+j-1} imes frac{j-2}{i+j-2} ]

两人都抓到黑的,中途跑出一只白的:

[f(i, j)leftarrow f(i -1, j-2) imes frac{j}{i+j} imesfrac{j-1}{i+j-1} imes frac{i}{i+j-2} ]

其他败的情况不计算内,复杂度 (O(w imes b))

「Luogu P1654」OSU!

update - 2020.8.3

考虑这样一个等式:((x+1)^3 = x^3 + 3x^2 + 3x +1),相比 (x^3) 多出了 (3x^2 + 3x +1)

(f(i)) 为前 (i) 个的期望分数。除此之外,还需维护一次方,二次方的期望值 ( ext{E}(x))( ext{E}(x^2))

对于一、二次方,有:

[ ext{E}(x) = ( ext{E}(x- 1) + 1) imes ext{prob}(i) \ ext{E}(x^2) = ( ext{E}[(x-1)^2] + 2 ext{E}(x-1) + 1) imes ext{prob}(i) ]

对于 (f(i)) 的计算存在两种情况,当前位置取或不取:

[f(i) = (f(i-1) + 3 ext{E}[(i-1)^2] + 3 ext{E}(i-1) + 1) imes ext{prob}(i) + f(i - 1) imes (1- ext{prob}(i)) \ = f(i-1) + (3 ext{E}[(i-1)^2] + 3 ext{E}(i-1) + 1) imes ext{prob}(i) ]

(O(n)) 递推即可。

「Luogu P3413」SAC#1 - 萌数

update - 2020.8.3

比较复杂的数位 dp。首先不能直接设 (f( ext{pos}, ext{isPa}, v_0, v_1)),而应设为 (f( ext{pos}, ext{isPa}, ex_0, v_0, ex_1, v_1))。其中从左至右分别为当前位置,之前是否已经构成回文,前二位是否有效(若为前导 0 则无效),前二位是什么数字,前一位是否有效,前一位是什么数字。多加了两维是为了防止形如 ( exttt{000041}) 这种前导 0 构成回文的实际非法的串混进来。

考虑用记忆化搜索实现。在枚举当前位的数字时,需要多计算一个 bool 值:下一次的回文情况(下一个 ( ext{isPa}))。

由于数位 dp 的重点根本不在方程而是实现,下面给出搜索部分的代码:

LL f[N][2][2][D][2][D];

LL solve(int pos, bool isPa, bool ex0, int val0, bool ex1, int val1, bool lim, bool zero) {
    if (!pos) return LL(isPa);
    if (!lim && !zero && (~f[pos][isPa][ex0][val0][ex1][val1]))
        return f[pos][isPa][ex0][val0][ex1][val1];
    
    LL ret = 0ll; int maxD = lim ? num[pos] - '0' : 9;
    for (int d = 0; d <= maxD; d++) {
        bool nxtPa = (val0 == d && ex0) || (val1 == d && ex1) || isPa;
        bool nxtZr = zero && (d == 0);
        bool nxtLm = lim && (d == maxD);
        (ret += solve(pos - 1, nxtPa, ex1, val1, !nxtZr, d, nxtLm, nxtZr)) %= mod;
    }

    if (!lim && !zero) f[pos][isPa][ex0][val0][ex1][val1] = ret;
    return ret;
}

「SDOI2016」征途

update - 2020.8.4

首先推式子,化简方差:(v = dfrac{1}{m}sumlimits_{i=1}^m v_i^2 - dfrac{1}{m^2}left(sumlimits_{i=1}^m v_i ight)^2),那么 (v imes m^2 = msumlimits_{i=1}^m v_i^2 - left(sumlimits_{i=1}^m v_i ight)^2)

这个式子的第二项显然不变,着重考虑第一项中的平方和部分。

(f(t, i)) 表示走了 (t) 段到位置 (i) 的最小平方和。那么朴素的转移:

[f(t, i) = minlimits_{j<i} {f(t-1, j) + [s(i) - s(j)]^2}\ s ext{为前缀和。} ]

这个 (O(n)) 一次的转移显然可以斜率优化。

具体的,我们设 (a < b)(a) 劣于 (b),那么:

[f(t-1, a) + [s(i) - s(a)]^2 > f(t-1, b) + [s(i) - s(b)]^2 \ downarrow \ dfrac{[f(t-1, a) + s^2(a)] - [f(t-1, a) + s^2(b)]}{s(a) - s(b)} > 2s(i)\ downarrow \ ext{slope}(a, b) > 2s(i) ]

然后用单调队列维护一个斜率递增的下凸包即可。复杂度 (O(nm))

「Luogu P1613」跑路

update - 2020.8.4

这是一个最短路问题,但真正的图并不是原图。我们设 (f(u, v)) 为结点 (u, v) 的联通情况,现在需要处理出这个图。

看到 (2^k) 就很显然是倍增。设 (g(u, v, t)) 为是否存在连接 (u, v) 长度为 (2^t) 的路径。那么可以用 dp 的方法处理出来:

[g(i, j, t) = operatornameorlimits_{kin ext{V}} { g(i, k, t-1) and g(k, j, t-1) } ]

对于 (f(i, j)) 的值,若存在一个 (tin[0, log_2( ext{maxlongint})]),使得 (g(i, j, k)= exttt{true}),那么 (f(i, j) = 1),反之则为 (infty)

最后在 (f) 上跑 Floyd 即可。时间复杂度 (O(n^3 log m))

后记

原文地址:https://www.cnblogs.com/-Wallace-/p/13385430.html