题目大意:
给定一列n<=200000个数,求冒泡排序k轮后的结果。
思路:
妙哉我把题面给的暴力写错了
好先看部分分
数据随机?好像一列数是递增的那么他们之间的相对顺序不会变,因此可以缩成一个点,每次把一个点中的最大值加入到下一个点当中。
一个优化是当当前点的最大值<下一个点的最小值的时候就把这两个点合并成一个。
好我就很开心的写了不是很多人会的左偏树开森
然后发现并没有必要用堆这个性质
直接用链表套数组随便xjb搞一搞就可以。
我花了两个小时成功的让我的代码多了一个log。真棒
然后开始考虑怎么做。
不难发现一个点的最终位置的计算是近似于下面的方法(细节略有出入)
首先看他前面有几个比他大的数字,并和k取min,记为p,这个就是这个点要向前移动多少位。
这个显然是可以用线段树/树状数组/平衡树来维护的。
手玩发现前p轮这个点每轮向前移动1.所以只有k-p轮可以用来向后移。
手玩发现这个点向后移多少,就是从它和之后第k-p个比他大的数字之间比他小的数字的个数。
怎么想到的就是,每次他都会停在上一轮第一个比他大的数 前一个位置。
然后发现尽管前面比他大的数字会跑到后面但是貌似没有影响。
发现这个显然可以用splay维护但是set里面没有这个函数。(牛神说可以用set水过?待补)
而我又不会写splay!!!
然后考虑用线段树。显然可以二分然后区间求和这样是两个log。直觉告诉我有更好的做法。
然后想了想我们可以再线段树上二分然后就开始写。快写完了才发现对于每个区间都可能要求区间和所以复杂度依旧是两个log
方,弃疗
然后想了想可以用主席树来维护,这样就可以在上面二分做到一个log。
但是只剩下不到一个小时所以继续弃疗。
sjk说他有30行代码的一个log做法。
我好方啊我去。
第二题丁神说什么行列式第三题田神说什么fft那我也是很无奈啊
关于这个题的两种做法。
第一种,观察每个点的位移,分为两部分,向前和向后。维护当前这个数之前有多少个数大于这个数,不妨记为c。
如果c>=k那么这个店向前位移k,显然,最终位置为i-k。
否则手玩返现就是这个点与之后第k-c个比他大的点之间比他小的数量。
上午写题的时候就发现这个询问区间不是整个区间,所以不能直接在线段树上二分,所以外面套一个二分就T了。
但是注意到离散化后如果强制使两个数字不同并且从大到小操作
那么这个数的之后第k-c个比他大的点就是序列上没加入这个点时序列上第k个数。
这个可以在线段树上而分求。最终位置是i-c+query(segment,k)-i-(k-c)=query(segment,k)-k。
然后输出即可。复杂度O(nlgn)。
第二种做法,注意到每个区间的最小值不会成为影响。
首先看[1,k+1],其中的最小值跑到了第一个,然后剩下的k个数字和第k+2个数字放一块取个最小值……
然后可以归纳证明这样是对的。
首先第一个点是显然正确的。
然后对区间位置归纳,假定当前处理到区间[L,R]然后[1,L]的序列确定,考虑新加入一个点,由于前面的序列已经确定那么这个区间显然也是正确的。
所以用堆来维护即可。复杂度O(nlgn)实测比线段树常数略小。