【CF737F】Dirty Plates 【构造】
翻译题解不易,求大家轻喷。
首先考虑 \(a=b=n\) 的情况。
显然 \(2\) 操作所移动的数必须是单增的并且应该与 \(s3\) 的前面连接上,并且如果 \(s2\) 的前缀符合这样的条件,那么我们应当立即执行 \(2\) 操作将它们转移过去。所以我们将 \(2\) 操作称为输出,每次操作后都应检查一下是否有机会输出,如果有机会则立即执行输出。类似的,对于 \(1\) 操作我们也有类似的限制,但要复杂得多:
首先,当 \(s2\) 中存在两个数 \(x,y\) 满足 \(y\) 恰好在 \(x\) 前面一位且 \(y<x-1\) 时不可能有合法方案,因为无法将 \([x,y]\) 之间的元素移进来,我们将这样的情景称为死胡同。
如果一个序列由若干个部分组成,每一段数都是连续的单调递增的自然数,并且后一段所有数都比前一段小,那么我们称这个序列是好的,它大概形如 \(x_1,x_1+1,x_1+2,\dots,y_1,x_2,x_2+1,\dots,y_2,x_3,\dots\),并且满足 \(x_1>y_2,x_2>y_3,\dots\)。考虑 \(s1\) 中最长的好前缀,显然将这个前缀移到 \(s_2\) 的过程中不能一起移它后面的元素,否则会形成死胡同。移一部分到 \(s2\) 就进行输出也是不可能,因此我们一定会将这个前缀通过若干次 \(1\) 操作后整个移动到 \(s2\) 再执行其他操作。
那么唯一的问题就是如何移动这个前缀了,我们分情况讨论:
- 如果这个前缀中出现的所有数是连续的,也就是说 \(y_2=x_1-1,y_3=x_2-1,\dots\),那么我们可以将各个部分依次移动到 \(s2\) 中形成一个单增的段,在适当的时间可以直接整段进行输出。序列的内部一定不会出现死胡同,只可能 \(y_1\) 与 \(s2\) 此时的开头产生死胡同,即当前 \(s2\) 的第一位 \(>y_1+1\),那么我们无论怎么移都会出现这个死胡同,因此我们只需要考虑用依次移动的方法能否成功。
- 否则,我们唯一的选择是将整个前缀整体移动过去。证明的话,设 \(x_k>y_{k+1}+1\) 最终将前缀分成若干段进行移动,如果没有任何一段同时包含了 \(y_k\) 与 \(x_{k+1}\),那么 \(y_k\) 所在的段会与 \(x_{k+1}\) 的段形成死胡同,否则,这一段一定会与前面后面的段都产生死胡同。
因此,我们可以通过 \(\mathcal O(n^2)\) 的每次找到最长的好前缀进行移动并判断能否进行输出来完成任务,因为我们始终采取最优策略,所以如果最终没有成功移走所有飞机那么一定没有答案。
当 \(a\) 与 \(b\)\(\not=n\) 时,输出不一定总是合法的,但如果每次可以执行输出时都立即执行,出现无法执行的输出就一定意味着失败,因此我们依然假设所有输出都可以执行,出现了不可执行的就说明没有答案。
依然检查最长的好前缀:
- 如果前缀中出现的数并不连续,那么我们依然只能整段进行移动,如果其长度 \(>a\) 则没有答案。
- 否则,因为每次移动的长度很重要,所以一段一段移动不一定最优,我们需要继续进行恶心的分类讨论:
- 如果每一段的长度都 \(\le a\) 且前缀的总长度 \(\le b\),依然采用之前的策略是最优的。
- 考虑前缀总长度 \(>b\) 的情况:
- 如果前缀由单独的一段组成,在输出时如果我们希望这一段与其他段一起输出,那么必然要求移动后 \(y1\) 在最底部或者 \(x1\) 在最顶部,而这是不可能完成的,因此我们只能选择在自己输出不与其他数扯上关系,而采取一个一个输入 \(s2\) 变成降序排列再依次输出确保了这一点能够完成,是最优选择。
- 如果前缀由 \(>2\) 段组成,那么只能依次整体移动每一段,否则总意味着死胡同,然后再看能不能输出。
- 如果前缀由 \(2\) 段组成,只有两种可行方案:先移动第一段的前面一部分再移动其他部分,或者先移动第一段和第二段的前面一部分再移动其他部分(都是移动两次),根据移动长度 \(\le a\) 的限制,我们容易列出一些不等式,又因为输出这一前缀时也不可能带上其他部分,所以任何满足不等式条件的移动方式的一样优,任取一个即可。
- 如果前缀总长度 \(\le b\) 但存在某一段长度 \(>a\):
- 如果前缀由单独的一段组成,一个一个移。
- 由两个以上段组成,没有合法方案
- 由两段组成,类似地列出一些不等式找合法方案。
我们同样可以得到以这样的操作方案进行操作,最后无法成功一定意味着没有答案的结论。因此我们 \(\mathcal O(n^2)\) 的模拟操作即可。