题意:N个数的排列,M次操作,每次求当前的逆序对数量并删掉一个数。
<一>先说一下cdq分治做法。(5960kb,1.4s)
网上很多题解,我都看不懂(其实很多人的程序几乎是一样的,就改了一下变量名),然后就自己硬着头皮想了这道题,基本是独立做出来的,做出来之后竟然1A,简直愉快。不过我太辣鸡了想了半天才发现这本质上是一个三维偏序,分别是时间,下标,数值,记为(t,x,y)。
我们可以把删除的过程倒过来,当做插入来做,时间t表示这个数是第几个插入的,显然给出的删除的点的t值依次是N,N-1,N-2...(越先删除的视为越后插入的)注意不在询问范围内的点的t值可以任意设置,并且显然没有哪两个点有相同的t或x或y值,这使得问题好考虑得多了。我们求的就是按顺序插入每一个数时,这个数左边比它大的、右边比它小的分别有多少个。形式化地,对一个点(t0,x0,y0),求出满足t<t0,x<x0,y>y0的点的个数记为lda[t0],满足t<t0,x>x0,y>y0的点的个数记为rxiao[t0]。
我想了一会儿,觉得最外层按x排比较科学,内部对t进行划分排序(相当于快排,将t值<=mid的划分到左边,同时对于划分到同一侧的点要保证原来的相对顺序不变),对y用树状数组来维护。每个节点[L,R]划分出来是这样的:
要找[L,mid]对[mid+1,R]的贡献:
先考虑对lda的贡献。枚举t∈[mid+1,R]的点(t0,x0,y0),区间内的点由于已经按时间划分好了,所以不需要考虑t<t0这一条件。只需找出左区间中x<x0且y>y0的点,由于两边的x值各自保持单调(如图),所以可以像树状数组求逆序对一样,将[L,mid]区间内的点的y值在树状数组上增加1,然后求[mid+1,R]的每个y值在树状数组上的前缀和即可。由于l1和l2都是单增的,这一操作复杂度为nlogn。
再考虑对rxiao的贡献:类似地,找出[L,mid]中x值大于[mid+1,R]中的x值的即可。
注意一层分治并不能找出[mid+1,R]中所有值的lda和rxiao,但整个分治一定会不遗漏不重复地覆盖每个点的决策区间。每层复杂度nlogn,总共logn层,总复杂度nlog^2n。貌似有人说cdq分治可以做到nlogn?我觉得不太科学,毕竟三维,不可能把某一维直接吃掉吧。。
cdq分治做这种题真是优秀,空间复杂度仅为O(n),时间复杂度也不逊于高级数据结构,并且分治(对半分)以及树状数组的常数都是极小的,基本是严格logn。
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
using n