集训游记 7.15-7.17 基础算法/动态规划

Day1

LOJ#6490. 「XXOI 2018」暑假时在做什么?有没有空?可以来学物理吗?

考虑 CDQ 分治.设当前分治区间为 \([l,r]\)\(\displaystyle mid=\frac{l}{r}\).考虑跨 \(mid\) 区间的贡献.
先更新 \([mid+1,r]\) 中的点的答案.枚举区间右端点,左端点在 \([l,mid]\) 中的部分形成一个区间.做一个前缀和后可以转化为区间 RMQ 问题.然后区间 \([l, mid]\) 中的点的答案同理.枚举区间左端点,然后右端点在 \([mid+1, r]\) 中的部分形成一个区间.

Buy Low Sell High

反悔贪心.对于某一天 \(i\),假设在这天卖出一只股票,肯定要在前面的某一天买进一只股票.假设买进天为 \(j\),则利润为 \(P_i-P_j\).按天数顺序扫描,用小根堆维护之前每天股票的价格.若选择当前天卖出股票,肯定优先选择股票价格最小的天数.加入反悔机制:若当前天选择了卖出股票,将 \(P_i-P_j\) 放入小根堆.

P5290 [十二省联考 2019] 春节十二响

7.15Test T4

考虑构造 \(p\) 进制线性基.由于 \(p\) 是质数,故任意一个非 \(0\) 的数在模 \(p\) 意义下的 \(k\) 倍可以张成剩余系中的所有数.故我们将所有数按行依次写开构造矩阵.将前面的位数保留成为自由元.然后贪心地使它们最大即可.

具体地,考虑插入一个数.跟异或线性基相同的,从高位开始枚举,若碰到不存在数字,并且自己哪一位非 \(0\) 的位数,将自己插入到线性基的那一位.否则将它的那一位消成 \(0\)

Day2

主要讲的是 DP.然后题补的不多.简单地记一下.

然后相对来说比较简单的题目就记一下大体思路.难题还是尽量给出实现.

做题记录以例题为主,之后会去补作业.

序列 DP

序列 DP 的题目.由于序列的拓扑结构相对简单,所以题目本身往往相对诡异.写题时容易无法下手.需要斟酌状态设计、转移顺序和优化方式.有时可以思考重排序列使得有贡献的转移集中在某一区间.

P7154 [USACO20DEC] Sleeping Cows P

由于保证了奶牛和牛棚大小互不相同,我们可以考虑把奶牛和牛棚放在一起排序:每个元素只能和前面的元素匹配,这个性质是有利于设计 DP 状态的.

先不考虑极大匹配的限制.记 \(F_{i,j}\) 表示前 \(i\) 个元素有 \(j\) 个奶牛未匹配(待匹配).按奶牛和牛棚分类转移即可.

再来考虑极大匹配的限制.那么每个奶牛就有两种选择:

  • 选择匹配,需要等待后面的某一个牛棚.
  • 选择不匹配,不允许后面有闲置的牛棚.

虽然一个奶牛选择是否匹配影响了后面的牛棚的抉择状态.但我们不妨在遍历到奶牛的时候就 提前决定 其是否选择匹配.为防止后效性,肯定是需要重新设计 DP 数组的.

\(F_{i,j,0/1}\) 表示前 \(i\) 个元素,有 \(j\)待匹配的 奶牛,是否存在选择不匹配的奶牛.这样的好处是遍历到牛棚时,容易检查出对于一种状态,它是否 必须和前面的某一个待匹配奶牛匹配

有转移方程:

\(i\) 是牛棚:

\[\begin{aligned} F_{i-1,j,0} &\rightarrow F_{i,j,0}\\ F_{i-1,j,0} &\rightarrow F_{i,j-1,0}\\ F_{i-1,j,1} &\rightarrow F_{i,j-1,1}(j>0) \end{aligned} \]

\(i\) 是奶牛:

\[\begin{aligned} F_{i-1,j,0} &\rightarrow F_{i,j+1,0}\\ F_{i-1,j,0} &\rightarrow F_{i,j,1}\\ F_{i-1,j,1} &\rightarrow F_{i,j,1}\\ F_{i-1,j,1} &\rightarrow F_{i,j+1,1} \end{aligned} \]

P8863 「KDOI-03」构造数组

操作次数显然是固定的 \(m=\displaystyle \frac{\sum b_i}{2}\)
那么我们将每个操作时的数组看成一列,会形成一个大小为 \(n\times m\) 的网格.然后每一列可以选择两个点 \(+1\).要求每一行恰有 \(b_i\)\(+1\) 的方案数.

设计 DP 状态,记 \(f_{i,j}\) 表示前 \(i\) 行有 \(j\) 列已经添入了两个 \(+1\).这样的好处就是添入一个 \(+1\) 和没有添入的列数都可以算出来.

转移枚举当前行有多少个 \(+1\) 填在了之前有一个 \(+1\) 的列上即可.
有转移方程:

\[f_{i-1,j}*\binom{m_{i-1,1}}{k}\binom{m_{i-1,0}}{b_{i-1}-k} \rightarrow f_{i,j+k} \]

其中 \(m_{i-1,1},m_{i-1,0}\) 分别表示前 \(i-1\) 行选择了 \(j\) 个有两个 \(+1\) 的列,剩下的中有一个 \(+1\) 和没有的列的个数.

CF1363F Rotating Substrings

非常神仙的线性 DP.循环位移一位很丑,我们可以想成:把某一位的字符扔到序列的前面某一位去.就有一种特别神奇的,从后往前 DP 的方法.

\(F_{i,j}\) 表示考虑 \(S\) 的后缀 \(i\) 通过将后缀中的字符扔到后缀中,或者挂起后缀中的某些字符,最终达到和 \(T\) 的后缀 \(j\) 匹配的最小挂起字符个数是多少.

这里 “挂起” 的意思是将某字符拎起来,但还不决定把它扔到哪.

\(T_j\) 的匹配方式分类可以得到:

  • \(S_i 和 T_j\) 匹配,有

\[F_{i+1,j+1} \rightarrow F_{i,j} \]

  • \(S_i\) 挂起,让后面的 \(S\) 匹配,有

\[F_{i+1,j}+1 \rightarrow F_{i,j} \]

  • \(S_i\) 去匹配后面的 \(T\),把后面 \(S\) 中挂起的等于 \(T_j\) 的字符扔到这里来和 \(T_j\) 匹配.

\[F_{i,j+1} \rightarrow F_{i,j} \]

这时要有 \(S\) 的后缀 \(i\)\(T_j\) 字符的数量大于等于 \(T\)\(j\) 字符的数量.

CF1188C Array Beauty

\(a\) 数组排好序.

发现 \(\min(|b_i-b_j|)=x\) 的子序列数量是难求的,但 \(\min(|b_i-b_j|) \ge x\) 的子序列数量容易求出的.记 \(num_x=\min(|b_i-b_j|) \ge x 的子序列数量\).有

\[ans=\sum_{x}num_x-num_{x-1} \]

这是我们已经可以做了.但其实它有更美观的形式.设 \(num'_x=\min(|b_i-b_j|) = x 的子序列数量\)

\[\begin{aligned} ans&=\sum_x x\times num'_x\\ &=\sum_x \sum_{i \le x} num'_x\\ &=\sum_i \sum_{x \ge i} num'_x\\ &=\sum_i num_i \end{aligned} \]

P7985 [USACO21DEC] Paired Up P

存在性质:交叉匹配一定不更优于不交叉的匹配.因为这样可以减小两对匹配的距离绝对值.或者这样说:若存在若干对匹配相交,肯定有一种合法的调整方案使得它们不相交.

考虑 \(T=1\).最小化是不需要考虑极大匹配限制的.因为一组非极大匹配肯定不能成为最优解:将其变成极大匹配答案肯定更小.我们可以记 \(F_{i,j}\) 表示前 \(i\) 头荷斯坦牛和前 \(j\) 头更赛牛的答案.有转移:

\[\begin{aligned} F_{i-1,j-1} &\rightarrow F_{i,j}(i,j配对合法)\\ F_{i-1,j}+cost_i &\rightarrow F_{i,j}\\ F_{i,j-1}+cost_j &\rightarrow F_{i,j}\\ \end{aligned} \]

再考虑 \(T=2\).这个就稍微有点麻烦了.将一头牛变成未匹配的时候,要保证它和前面未匹配的牛不能形成匹配.于是考虑改变状态.
\(F_{i,j,0/1}\) 表示处理完前 \(i\) 头荷斯坦牛,前 \(j\) 头更赛牛,然后下一头 荷斯坦牛/更赛牛 可以计入贡献的答案.

尝试理解一下这个抽象的状态:就是说下一头 荷斯坦牛/更赛牛 和前面的所有 更赛牛/荷斯坦牛 全部无法匹配.转移时分类讨论:

  1. 保留待贡献的牛的种类.

\[\begin{aligned} F_{i,j,0}+cost_{i+1} &\rightarrow F_{i+1,j,0}\\ F_{i,j,1}+cost_{j+1} &\rightarrow F_{i,j+1,1}\\ F_{i,j,0/1} &\rightarrow F_{i+1,j+1,0/1}(i,j配对合法) \end{aligned} \]

  1. 尝试改变待贡献的牛的种类.

\[\begin{aligned} F_{i,j,0} &\rightarrow F_{i+len,j+len,1}\\ F_{i,j,1} &\rightarrow F_{i+len,j+len,0} \end{aligned} \]

每次找出某一头 荷斯坦奶牛/更赛牛 位置之后的,第一头不能与它匹配的 更赛牛/荷斯坦牛 的 前一个位置,并尝试转移到它那里来.

树形 DP

有点遗憾没讲到换根 DP,反倒是图论讲缩点的时候谈了一下.

CF1778F Maximizing Root

首先有结论,自底而上操作的顺序肯定更优.这符合我们自底而上的 DP 顺序.

最大化 \(a_1\),其实是最大化 \(a_1\) 最终乘上的倍数.如果可以乘上倍数 \(d\),必须满足 \(d\)\(a_1\) 子树的因数.正常的想法,应该设计状态考察耗费一定代价能获得的最优因数.但发现子树中最大的因数,从全局来考察不一定在最优.那么我们必须把每一个因数都纳入考量.使用一个改变状态的小技巧:把 DP 的答案纳入 DP 状态中,DP 答案的最小花费,而不是某一花费上的最优答案.而花费的优劣是容易抉择的.

具体地,我们设 \(F_{u,i}\) 表示满足 \(u\) 子树存在公因数 \(i\),子树中需要操作的最小次数.

对于不操作 \(u\) 的情况,\(F_{u,i}\) 是将子树的 \(F_{v,i}\) 逐点相加.对于操作 \(u\) 的情况,即 \(F_{u,i}+1 \rightarrow F_{u, i*i}\)

考虑缩小状态数,发现使 \(a_1\) 可以进行操作,必须使得子树存在因数是 \(a_1\) 的因数.而 \(a_1\) 的因数在 \(1e5\) 的范围内至多只有 \(128\) 个.

P8352 [SDOI/SXOI2022] 小 N 的独立集

相当于在树上最大独立集的外壳假象下的计数.容易想到状态 \(F_{u,i,j}\) 表示 \(u\) 子树,勒令 \(a_u\) 不选和勒令 \(a_u\) 选的答案.转移时考虑添加一颗新子树.

这时有技巧 DP 套 DP!就是说状态合并时,转移的新状态需要由原来状态通过某些决策 DP 出来.考察 \(F_{u,i,j},F_{v,i',j'}\) 合并.指向的状态为 \(F_{u,max(i+i',i+j'),j+j'}\)

非常优美非常和谐!状态数爆炸!

继续寻找性质:发现勒令不选的答案不会比勒令选的答案小很多.事实上,勒令选的答案去掉 \(a_u\),就是一种合法的勒令不选的答案.我们新设计一种树上最大独立集的 DP 状态:\(G_{u,0}\) 表示子树 \(u\) 勒令不选 \(u\) 的答案,\(G_{u,1}\) 表示子树 \(u\) 不管是否选择 \(u\) 的答案.有 \(G_{u,0} \in [G_{u,1}-a_u, G_{u,1}]\).我们只需要记录一个值,和两个值的差即可!

再来简要分析一下树上背包的时间复杂度:

for(int v:e[u]){
    dfs(v, u);
    for(int i=1; i<=sz[u]; i++){
        for(int j=1; j<=sz[v]; j++){
            ...
        }
    }
    sz[u]+=sz[v];
}

考虑一份这样的树上背包,其实在实现一个这样的过程:
枚举子树 \(v\) 和之前子树的贡献,将子树 \(v\) 并入之前的子树.两层循环实际在枚举 \(lca\)\(u\) 处的点对.每次枚举的 \(i,j\) 可以一一对应到树上的点对上,故总时间复杂度为 \(O(n^2)\)

而对于此题,由于还要枚举额外的增量数组.综时间复杂度为 \(O(n^2m^4)\)

Swap and Maximum Block

\(\red{还没补.}\)

根据序列建出 trie 树.

发现交换每两个相邻的 \(2^k\) 区间等价于在 trie 树上交换所有子树宽度为 \(2^{k+1}\) 的节点的两个儿子.

对于 \(k\) 级子树 \(u\),其子树的最大深度为 \(n-k\).子树内每层是否交换的数量总和为 \(2^{n-k}\).而 \(k\) 级子树一共约有 \(2^k\) 个.故 \(k\) 级的总状态数为 \(2^k \times 2^{n-k}=2^n\).所以 \(n\) 层的状态一共有 \(n2^n\) 种.

状态压缩 DP

有一道 Codeforce 训练场的题,但主要是乱用 bitset 瞎搞.有时间去补一下.

P7142 [THUPC2021 初赛] 密集子图

主要难在刻画最短路.我们把原图最短路小于等于 \(i\) 的部分,集合 \(S\) 划分成两部分:最短路等于 \(i\) 的部分 \(S_1\) 和 小于 \(i\) 的部分 \(S-S_1\).考虑加入最短路为 \(i+1\) 的部分 \(S'\).发现对于 \(u\rightarrow v (u\in S-S_1,v\in S')\),连边必须为白色.对于 \(u \rightarrow v(u\in S_1,v\in S')\),连边对于每一个 \(v\) 至少存在一个为黑色.然后就可以转移了.

\(F_{i,S_1,S_2}\) 表示考虑了所有最短路 \(\le i\) 的点,其点集为 \(S_1\),其中最短路恰好为 \(i\) 的点集为 \(S_2\),发生这种情况的概率.

存在转移

\[F_{i,S_1,S_2}\times P(S_1-S_2 \rightarrow S)\times P'(S_2\rightarrow S) \rightarrow F_{i+1,S_1\cup S,S}(S_2\subseteq S_1,S_2 \neq \empty) \]

然后我们预处理 \(P,P'\) 即可.

P7519 [省选联考 2021 A/B 卷] 滚榜

又是一道代价提前计算的好题!
发现让我计算可能排列的个数:一种排列只能算作一次.

考虑状压队伍的集合,然后按揭榜的顺序 DP 是较容易的.由于 \(b\) 的取值不确定又影响答案,必须要计入 \(DP\) 的考量范围,我们考虑对于任意一种排列对应到唯一的一种 \(b\) 的数列上,这样就能保证不算重.

但同时不能漏掉可能的排列:要保证任意一种可能合法点排列在我们的映射方案上都是合法的!

贪心.顺序考虑揭榜的每一个队伍,在保证其能达到当前榜首的前提下,确保其 \(b\) 尽量小.这样的构造方法就完美符合了我们的要求.

区间 DP

P4563 [JXOI2018] 守卫

突破口非常奇怪.但又很合理.

考虑一个性质:守卫只能向左边看.那么对于一个区间 \([l,r]\),要使得在这个区间中放置守卫使得所有地方被看见,区间的右端点 \(r\) 一定需要放置守卫.那么设 \(r\) 在区间中能看见的最左的地方为 \(p\).可以证明:\(\forall i\in[p,r]\),不存在一个地方 \(j\in[l,p-1]\),使得 \(i\) 可以看到 \(j\).具体来说,在 \((p,r)\) 之中的点一定存在于 \(p,r\) 连线的下方.若某一点可以看到 \(p\) 左边的点,那么 \(r\) 一定也能看到.如果画一张图就会非常清楚.

那么这个区间就被我们划分成了两部分:\([l,p-1],[p,r]\).这两个部分互相没有关联.设 \(F_{l,r}\) 表示看守 \({l,r}\) 需要的最多人数.那么我们就有了转移:

\[\begin{aligned} F_{l,p-1}+F_{p,r}\rightarrow F_{l,r}\\ F_{l,p}+F_{p,r}\rightarrow F_{l,r} \end{aligned} \]

还有一个细节.那就是当 \(p=l\) 的时候,这个转移就不成立了.那我们再考虑第二靠左的,能被 \(r\) 看见的地方即可.

CF1178F2 Long Colorful Strip

这个区间 DP 思路还比较自然.

首先想到可以离散化.一次划分顶多产生两个颜色不同的分解点.如果离散化之后的数组长度 \(> 2n\),可以直接判断无解.

考虑设计 DP 状态.设 \(F_{l,r}\) 表示把区间 \([l,r]\) 按颜色顺序覆盖完的方案数有多少种.

转移时可以确定最小颜色围出的区间肯定要覆盖最小颜色.然后我们枚举覆盖的区间向左右延伸多少即可.这样会存在许多非法的状态.设最小颜色围出的区间是 \([l',r']\).考虑存在另一种颜色在 \(l'\) 的左边出现并且在 \(r'\) 的右边出现之类的情况,其实是无解的.那我们可以做一个这样的特判:当最小的颜色在数组中的出现位置不完全包含于当前的区间中,我们认为当前区间无解.这样就能规避掉非法的状态了.

P9129 [USACO23FEB] Piling Papers G

另辟蹊径的数位 DP.

首先答案是可差分的.那么求坐落于 \([A,B]\) 之间的答案就相当于求 \(\le B\) 的答案减去 \(\le A-1\) 的答案.那么我们考虑如何求出形如 \(\le T\) 答案.

像常规方式一样记录 \(F_{i,0/1/2}\) 表示考虑已经填入的低 \(i\) 位和 \(T\) 的比较结果是 小于/等于/大于.那么当实现转移 \(\overline {a_i+x}\rightarrow x\) 的时候比较结果是容易更新的,但实现转移 \(\overline {x+a_i}\rightarrow x\) 的时候由于前面确定的位数都要向更高位挪动一格,比较的结果就不能确定了.所以这个状态是不可取的.

同样是 提前计算/提前确定 的思想,我们设 \(F_{l,r,0/1/2}\) 表示填入了在 \([l,r]\) 中的位数,和 \(T\)\([l,r]\) 中比较的结果为 小于/等于/大于 的状态数.那么向左添一个就相当于 \(l-1\),向右填一个就相当于 \(r+1\).同时大小的比较也是很容易确定的.注意到 \(q\) 的数量级是 \(O(n^2)\).我们预处理出所有区间的答案,然后 \(O(1)\) 回答即可.

其它 DP 题目

P7967 [COCI2021-2022#2] Magneti

抱き寄せて欲しい 確かめて欲しい
想要被拥入你怀中 想要确认心意

間違いなど無いんだと 思わせて
让我知道 没有误会了什麼

首先按 \(r\) 从小到大排序.那么新添加一个磁石只需要考虑不吸引别的磁石即可.

设计状态就需要一些奇思妙想.设 \(F_{i,c,l}\) 表示考虑前 \(i\) 个磁石,形成 \(c\) 个联通块,耗费的总长度为 \(l\) 的方案数.

然后考虑添加一块磁石可能的三种变化:

  1. 独立为一个联通块:

\[F_{i,c,l}\rightarrow F_{i+1,c+1,l+1} \]

  1. 和其中的某一联通块合并:

\[F_{i,c,l}\times 2n\rightarrow F_{i+1,c+1,l+r_{i+1}} \]

  1. 和其中两个联通块串起来:

\[F_{i,c,l}\times 2\binom{c}{2}\rightarrow F_{i+1,c-1,l+2r_{i+1}} \]

那么最后答案肯定是 \(\sum_i F_{n,1,i}\)

posted @ 2023-07-15 10:17  ckain  阅读(149)  评论(1)    收藏  举报