dp?
感觉这次是真的可以,写一篇 dp 的东西,但其实还是一个做题记录之类的东西?
贡献延迟计算
在 dp 的过程中你通常会为了避免后效性而在一个位置决策好这个位置的所有贡献、方案,但可能为了避免后效性会使得你的状态数很大,需要记录很多信息。
而我们可以通过延迟计算贡献,即选择时不算贡献,留到后面某个位置计算,或者把贡献拆到后面的位置上。也就是,我们可以延迟确定该位置选择了什么,也可以延迟计算选择的贡献。
AT_arc207_a [ARC207A] Affinity for Artifacts
之前已经写了匹配的做法,考虑一下 dp 延迟钦定怎么做。
我们先假设每次
考虑对于一个
令
分类讨论
P14364 [CSP-S 2025] 员工招聘
dp 贡献的延迟决策,可能写的有点绕,但是感觉我可能就是这样理解明白的。
怎么叫延迟决策呢,考虑最终我们要怎么算方案数,对于一个方案假设最终拒绝了
其实相当于我们把
于是在决策的过程中我们可以时刻把所有人划分为两个集合
由于前缀中失败的人数是单调不降的,因此人只会从集合
考虑一个从前向后决策的过程需要记录哪些信息,当前失败的人数
考虑最终
考虑转移,转移时我们需要决策第
令
若
这部分方案数为
- 若该位置放一个空位,有转移
{a_{j+1} \choose t} {k \choose t} t! f_{i,j,k} \to f_{i+1,j+1,k-t+1} - 若该位置放一个
c_p \le j+1 的p ,考虑选出这个p 的方案数为b_{j+1} - (i - (k-t)) ,于是有转移{a_{j+1} \choose t} {k \choose t} t! (b_{j+1} - i + k - t)f_{i,j,k} \to f_{i+1,j+1,k - t}
若
否则预留一个空位给
要求通过人数
时间复杂度
不知道哪里的题
不是,这个 OJ,这么神秘吗。
根据我们前面总结的东西,可以把所有决策划分为类似两个集合,考虑按照值域扫描,维护
- 将
i 匹配给某个t_i = 1 的x ,记录a_i 表示x\le i,t_i = 1 的位置数,b_i 表示x \le i,t_i = 0 的位置数,我们知道x \le i,t_i = 0 的匹配的一定是\le i 的值域,从而可以计算出x \le i,t_i = 1 匹配了的数量为j-1 - b_{i-1} ,未匹配的数量即为a_i - (j - 1 - b_{i - 1}) ,转移f_{i-1,j-1} \to f_{i,j} 。 - 将
i 匹配给某个t_i = 0 的x ,此时我们先不计算贡献,而是先给x 匹配一个黑盒,后面再决策什么东西填进黑盒里,转移有f_{i-1,j} \to f_{i,j} 。 - 给
x = i,t = 0 的x 计算贡献,从前面所有未匹配的数中选择一个填进去,系数为(i - j + 1) ,转移f_{i-1,j - 1} \to f_{i,j} 。
真不知道哪里的题
给定
-
-
- 对于
1 \le i \le n ,满足\text{mex}(A[1\dots i]) \le mex(B[1\dots i]) 。
咕咕咕了,真的就是完全没听听懂啊。
AT_utpc2023_p Priority Queue 3
挺不错的延迟决策的题,感觉难的延迟决策其实难点就是你不知道要记多少信息,哎其实 dp 都是吧就是,不知道该设啥。
考虑记录
考虑对于
考虑第 + 的次数,- 的次数。分 + 和 - 讨论。若该位为 +,考虑加入一个什么样的数:
- 加入一个不在
a 中的数,此时一共有n - a_j - (m - j) 个大于j 的非a 数,其中堆中有x - y - k 个,于是系数为n - a_j - (m - j) - (x - y - k) ,有转移f_{i,j,k,l} \to f_{i+1,j,k,l} 。 - 加入
a_j ,此时要求l 从0 变为1 ,k 变为k+1 。 - 加入非
a_j 的a 中的数,我们暂时不决策加入的是什么数,把方案数的贡献延后计算,于是转移f_{i,j,k,l} \to f_{i+1,j,k+1,l} 。
若该位为 - 有:
- 若
k = 0 ,显然堆中都没有数,不合法。 - 若
k > 1 ,弹出的一定不是最大值,也就是任意一个a_j ,并不关心他们是什么,留到后面计算贡献。 - 若
k = 1 且l = 0 则弹出的不是最大值,同样不需要关心弹出的是什么。 - 否则
k = 1 且l = 1 ,此时弹出的时最大值,我们来计算在这段中产生的贡献,设下一个最大值的位置为p ,首先>p 的a 中的数一定已经在j 之前被弹出了,而有c = b - (m - j) 个在前面弹出了,但还没有决策他们是什么数,我们需要选出d = j - p - 1 个,作为a_j \sim a_p 之间的数,于是要乘上一个A_{c}^d 的系数。
然后我们解决了这个题,看上去复杂度是
很牛的题。哎不是,这题怎么是,小根堆(
贡献拆分
贡献拆分其实和延迟钦定很像,某次决策会产生若干的贡献,而我们把这个贡献拆分给后面被他影响的每个决策上面。比如某次操作贡献为一个增量,我们通过枚举或记录一些东西,从而在这个位置直接算出增量对后面的全部贡献。
线头 dp
线头 dp,其实就是连续段 dp 吧,考虑一些路径问题,比如要求走所有点一次最小化贡献,如果你发现贡献可以独立的拆到每个点上,也就是我们不关心是从哪个点走过来的,就可以记录非常少的信息,而简单维护贡献。
通常贡献是由相邻两项之间产生的,通过 dp 维护已经合法的一些连续段的信息,从而使得新加入一个点时可以合并、延长、新增一个段,状态通常只需要记录段数以及一些边界信息。
感觉说不明白,看个题可能就懂了。
CF704B Ant Man
考虑我们只关心相邻两个点之间产生的距离贡献,这个贡献取决于大小关系,我们从小到大决策每个点放的位置,这样可以确定好大小关心,从而只需要决策这个点在操作序列中位于哪个位置。
我们对操作序列(经过的点的顺序)维护连续段,则
-
-
- 接在右边同理。
- 合并两个连续段,左右均作为较大的产生贡献。
要注意进行各个操作均会存在一些限制,比如