bzoj 2457

原题链接

题意

依次处理给定的\(n\)个数,对于每个数,能做以下两件事:

1.新建一个双端队列,并将当前数作为这个队列中的唯一的数;

2.将当前数放入已有的队列的头之前或者尾之后。

对所有的数处理完成之后,要求这些队列排序后可以得到一个非降的序列。

求解最少需要的双端队列数。\(1 \leq n \leq2 \times10^5\)

心路历程

1个双端队列合法,(假设没有相等元素,设首个入队的数为\(first\))当且仅当大于\(first\)的数由小到大,其在原操作序列中的下标由小到大;小于\(first\)的数由小到大,其在原操作序列中的下标由大到小。

并且所有双端队列连缀起来非降,故各个队列内部不能出现断层。

假如没有相等的元素,很容易得到每个双端队列的极大情况,当每个双端序列都取极大情况时答案自然最小。

假如有相等的元素,意味着与\(first\)相同的元素取的个数不定,放的位置不定,感觉情况很多,并不会做。

题解

将原数组按照数值大小排序。假设确定了各个双端队列的最终形态。那么一定满足:各个双端队列中,元素在操作序列中的下标先单调下降再单调上升。下文称单谷性质。

问题转化为我们要将按数值排序后的下标序列划分成尽量少的单谷。

不妨按照数值从小到大的顺序考虑,逐步进行最优划分。

看上去跟”心路历程”的做法差不多,实际上这种思路更为成熟全面。按数值大小顺序考虑划分,与最终目标相契合,避免了按照操作序列考虑时,极大情况存在多种、不知道边界值划分至何处、可能导致区间增多等问题。例如按操作序列考虑时,若将6划分给第1个区间,[3,3,6,6]固然极大,但却非最优,因为剩余\([0]\)和[9]两个序列。

对于相同的数值,我们仅可以知道它们在操作序列中的下标集合;至于映射到的具体下标,根据单谷的需要可以随意调整。

我们贪心地令每一个单谷尽可能地长;且考虑到相同数值的特殊性,故做法如下:

用一个变量记录当前队列处于递增还是递减状态,并且记录末端元素。将每一段数值相同的区间拿出来考虑,尽可能地延续单谷,对于无法延续的元素另开队列,计数。

具体地:

假如原本递减,且存在大于末尾的数,转递增,记max;否则递减,取min。

假如原本递增,如果存在小于末尾的数,则新开1个,取最小值为min,处于递减,ans ++。

时间复杂度\(O(n)\)。[代码见此](https://github.com/littlewyy/OI/blob/master/bzoj 2457.cpp)

回顾与思考

关键是从最终状态出发有序思考,便于简化问题。

思考性质看透本质,有助于解题。

posted @ 2019-08-05 10:09  littlewyy  阅读(208)  评论(0编辑  收藏  举报