营业日志 2020.11.7 一次信息不对称引发的 CSP2020 T4 讨论
Elegia
·
2020-11-07 23:29:13
·
个人记录
起因是在场外得到题意之后口胡 T4 的过程中,并不知道题目有这么一个性质:
保证每组数据(包括所有修改完成后的)的 a_i 以不降顺序排列。
经过了一些讨论,还是确认了如果不保证 a_i 有序该怎么做。首先主要思路还是模拟出如果所有人都进行决策会发生的过程。
如何排序?
这个问题其实较好回答,我们只需最开始进行一次长为 n 的排序,接下来我们每次只需对修改的 k 个数单独排序,而与剩下的数按照原顺序归并即可。记排序时间为 \mathsf S(n) ,那么这部分的时间是 \Theta(\mathsf S(n) + T(n + \mathsf S(k))) 。
接下来的过程我们分为两个 pass。
pass1
第一个 pass 是为人熟知的「NOIP2016」蚯蚓中所出现过的,我们用两个队列 \texttt Q_{\texttt 1},\texttt Q_{\texttt 2} 来维护一个“单调队列”,其中将排好序的贪吃蛇初始全部放入 \texttt Q_{\texttt 1} ,然后将每次得到的新的贪吃蛇推入 \texttt Q_{\texttt 2} 。
我们设每次推出的最小,最大值分别是 f_j, b_j ,推入的值是 u_j=b_j-f_j ,那么如果 b_j \ge 2f_j ,就总有 f_j \le f_{j+1} 。注意到 b_j\ge b_{j+1} 是必然的,那么在时刻保证 f_j\le f_{j+1} 的情况下,我们就保证了 u_j\ge u_{j+1} ,也就是 \texttt{Q}_{\texttt 2} 中的数单调递减 。那么这是值的情况,在考虑编号的情况下还是否保字典序呢?注意到 u_j=u_{j+1} 当且仅当 b_j=b_{j+1} \wedge f_j=f_{j+1} ,此时因为 b_j=b_{j+1} ,那推出的编号是递减的,所以此时 u_j,u_{j+1} 对应编号也递减。
当第一次发生 b_j < 2f_j 的时候,我们进入第二个 pass 的讨论。
pass2
我们不妨记现在整个数列是形如 a_1\le a_2\le \dots \le a_n 且考虑编号的情况下严格递增,额外的,有 2a_1 > a_n 。那么我们不难得到如下链条:
第一次推出的数值 为 a_1, a_n ,推入的数值 为 a_n-a_1 < a_1 。
第二次推出的数值为 a_n-a_1, a_{n-1} ,推入的数值为 a_{n-1}-a_n+a_1\le a_1 。
第三次推出的数值为上一轮推入的数和 a_{n-2} ,推入的数值为 (a_{n-2}-a_{n-1})+(a_n-a_1) \le 0 + (a_n-a_1) <a_1 。
……
施加归纳法不难证明,第奇数次推入的数值均小于 a_1 ,此时它必然是最小的数,因此他们会在下一轮推出。第偶数轮推入的数值均 \le a_1 ,且其数值 最小,会在下一轮推出一个同数值 的数。
现在我们对数值的走向已经完全明晰了,但魔鬼在细节处,请端详如下构造:
a_1 = a_2 = \dots = a_m < a_{m+1} = \dots < a_{n-3} = a_{n-2} < a_{n-1} = a_n < 2a_1
不难注意到,这一数据在偶数时间所推入的数据均 =a_1 ,而我们可以通过任取排序前 a 的位置情况来控制在 a_1 数值所存在的下标集合的分布情况。对此时值为 a_1 所维护的下标集合 \texttt I ,初始 \texttt I 中有一些元素,接下来会循环执行如下过程:
给出下标 p ,令 \texttt{I.push(p)} 。
取出最值 \texttt{I.pop()} 。
由于在我读错的这个题意下可以任意排列 a ,我们显然需要解决这个比较一般性的上述问题。(对于原题意来说,似乎也可以通过在 pass1 过程的约束来在一定的自由度下操纵 \texttt {push} 的下标顺序,但是性质比较奇怪)
这个堆问题容易启发我们思考能在什么程度上进行离线。容易发现我们可以分别处理 a_{m+1} \sim a_n 的情况和 a_1\sim a_m 的情况。
对于前 m-n 次,我们每次取出的最大值必然是 a_{n-i+1} ,因此这也在询问所有的 \texttt {pop} 之前就确定了这部分 \texttt{push} 的所有 p 。
对于最后的操作,我们容易发现无非两种情况:
最小值在 0, a_1 之间跳转,而相当于此时的偶数次操作等于没有改变 \texttt I ,而奇数次操作同时移除了最大值和最小值。
此时有一个在 (0, a_1) 间的数 x ,它会反复和当前最大的下标得到 a_1 -x, x, a_1-x, \dots ,这个维护也是 trivial 的。
因此,我们可以对前 m-n 次操作进行离线。由于下标都是 [1,n] 之间的整数,可以先进行基数排序,然后从小到大看每个被 \texttt{push} 的下标会作为哪个 \texttt{pop} 的答案。我们注意这个问题还可以通过问题的特殊性简化,这来源于操作序列的结构:操作序列是形如
\texttt{uuuuu}\cdots\texttt{uouo}\cdots\texttt{uo}
其中 \texttt {u},\texttt{o} 分别表示 \texttt{p{\color{red}{u}}sh},\texttt{p{\color{red}{o}}p}
的序列,我们会发现被确认的答案只会占据一个前缀以及后面的若干个相邻的 \texttt{uo} 对。因此在这个离线处理的过程中,我们只需维护前缀所在的位置 p ,按顺序考虑的 \texttt u 如位置 \le p ,则需要将 p 扩展到下一个未被确认的 \texttt o ,否则该 \texttt u 所在的右边的 \texttt o 必然未被确认。
综上,我们在排序后的处理得到了一个 \Theta(n) 的算法。