例1:排序只能交换相邻数字,最少交换次数为逆序对数。
证:每次交换能使逆序对数不变或-1,且排序完成与逆序对数=0等价。此外,只要存在逆序对就会存在相邻逆序对,交换相邻逆序对使逆序对数刚好-1。
例2:区间顺逆排序,询问位置的值。
做法:二分答案,取01“暴力”,时间O((n+mlogn)logn),空间O(n+m)。
例3:比较网络(规模为n的输入及输出的黑箱,内部只有比较置换器)是否为排序网络,有多少个序列能够成功排序。
做法:所有的01序列,是否都能够成功排序,如果都可以就可以,因为当且仅当所有数k排序后<k的数都在k之前才行。计数则可以思考渐变的01序列,必须每一个都合法,像middle那样,就是路径计数O(n2^n)了。
题意:对于一个长为n的数组,定义一个m个步骤的算法,第i步将a[li~ri]升序排序。共询问q次,每次给出一个这样的数组,询问算法能否将数组成功排序。
20%:n≤100,200 m≤100,200 q≤100,200
直接暴力,时间O(qmnlogn),空间O(m+n)。
20%:n≤1400,1500 m≤9e5,1e6 q≤8
用set保存相邻逆序对。m次操作每次维护相邻逆序对的变化,区间排序后逆序对不会增多,逆序对最多O(n^2)个,时间O(q(n^2+m)logn),空间O(n^2+m)。注意到这种做法也可以用来模拟例1的过程!
10%:n≤1300 m≤8e5 q≤1300 序列为01序列且1的个数一定
最终排序成功的结果是唯一的,从结果逆推(将区间随便shuffle)就可以得到所有的最初输入但规模是指数级的。稍作证明,可以发现解的优劣以及每一步的最劣结果是存在的(这里的推理很强!!!)。预处理找到最劣解,使用例2的线段树是O(n+mlogn)的,后续的check是O(n)的。
20%:n≤1400,1500 m≤4000,5000 q≤1400,1500
30%:n≤1200,1400,1500 m≤7e5,9e5,1e6 q≤1200,1400,1500
技巧:将序列离散化为排列,相同的数则下标较小的取较小的值,逆序对的情况不变所以是正确的。
考虑排序网络,相当于其n-1个(≥2~n)01序列都可以成功。
于是需要预处理出1出现各种次数的worst解,直接暴力复杂度无法接受,于是可以考虑整合,直接对1~n的序列求worst解然后依次化成01序列是完全等价的。检查的时候依次检查,如果变成一个+1一个-1若前缀和为正为负则不可行。预处理使用消逆序对O((n^2+m)logn),暴力预处理O(mnlogn),使用段数合并拆分结构也可以但金策不会证复杂度。每次check复杂度O(nlogn)。
暴力优化:去掉完全无效的操作,使用n~1的排列模拟,如果无用则删去。
造操作:
20%朴素模拟可过随机构造平均化AC与WA;
20%的q较小但q太小也不行逆序对,且要让操作的区间足够长且不存在无用操作而且不能过早结束排序(留下边上几个元素最后才操作);
20%的m和n小可以用暴力预处理,构造可以递归构造O(n)个操作O(n)长度的区间O(n^2)的逆序对,卡掉错误做法;
10%+30%中有两种,第一种为第二个20%,第二种几乎只修改相邻元素O(n^2)个有效状态暴力即O(qn^2)。
造询问数组:
造完操作后,处理出worst解,然后随机调整。
生成AC的就交换较近逆序对;生成WA的就生成较近逆序对。
WA的结果和AC很相似,防止随机水过,再掺杂一些极端糟糕的数组。