BZOJ3295 CQOI2011 动态逆序对

3295: [Cqoi2011]动态逆序对

Time Limit: 10 Sec  Memory Limit: 128 MB
Submit: 2263  Solved: 721
[Submit][Status][Discuss]

Description

对于序列A,它的逆序对数定义为满足i<j,且Ai>Aj的数对(i,j)的个数。给1到n的一个排列,按照某种顺序依次删除m个元素,你的任务是在每次删除一个元素之前统计整个序列的逆序对数。

Input

输入第一行包含两个整数nm,即初始元素的个数和删除的元素个数。以下n行每行包含一个1到n之间的正整数,即初始排列。以下m行每行一个正整数,依次为每次删除的元素。
 

Output

 
输出包含m行,依次为删除每个元素之前,逆序对的个数。

Sample Input

5 4
1
5
3
4
2
5
1
4
2

Sample Output

5
2
2
1

样例解释
(1,5,3,4,2)(1,3,4,2)(3,4,2)(3,2)(3)。

HINT

N<=100000 M<=50000


这道题显然是裸题,不过即使是裸题还是有一点点价值的,因为这种题的做法比较多,可以多练习下模板(捂脸)。


做法1:主席树,这个很显然,不用多说了,PS:因为M是N的一半,所以要静态建树,修改M次时再套用树状数组,空间复杂度是MlogNlogN,否则是NlogNlogN,会爆空间(不要问我为什么知道,我不想说=.=)


做法2:二维线段树,和主席树差不多啊,同样要注意空间问题。


做法3:树状数组套平衡树。如果平衡树选择的是Splay就要注意很多问题:同样要先静态求出答案,因为splay的常数比较大,同时插入的时候要打乱顺序,因为Splay若是插入有序的数列复杂度会退化(话说是不是我写的不对?)。所以比较好的选择是用Treap,常数很低,复杂度稳定,直接倒过来插入N个数就行了,简单粗暴,代码100行都不要(主席树和Splay的我写了150行)。


做法4:分块,这个代码就更短了,不过复杂度堪忧,Nsqrt(N)log(sqrt(N)),不过令人高兴的是BZOJ貌似是只看总时间的(?),所以可以水过,PS:分块的总时间看来并没有比前面几种算法慢,是数据水还是复杂度可以特殊分析?


做法5:显然后面的插入对前面的询问不会有影响,且这个问题的本质是计数问题,所以插入之间互相不影响效果,即每个插入对答案的贡献是独立的,所以我们可以采用按时间分治的算法,将操作(一个点的插入和询问可以分成两个)分成两部分,递归处理后再计算前半部分的插入对后半部分的查询造成的影响。那么我们将原问题转化为:

给定平面上一些点,再询问若干个点,回答询问的点左上角(右下角的可以分开来做,也可以合起来)(即坐标在他之前的,值比他大)有多少个点。那么我们此时将询问和给定点再看成一些操作并按X坐标排序,我们发现该问题仍然满足之前的性质,即X坐标大的插入不影响X坐标小的询问,那么我们再次运用按时间分治,将问题继续简化为:

给定一些数和一些询问数,对于每个询问回答比他大的数有多少个。显然若将询问和插入排好序,则可以线性回答,我们发现在分治时已经将该区间的左右分别排序,那么怎么得到这个区间的排序呢?这个问题是不是很熟悉,对!就是归并排序,同时归并排序的本质也就是按时间分治。

那么处理最后一个问题的复杂度就是O(N),同时我们套用了两层分治,总复杂度为NlogNlogN,由于分治的常数特别小,而且空间占用为O(N),所以该做法的空间,时间,编程复杂度均小于前面几种做法。(CDQ大法好,离线大法好)


至于代码呢,我当然是没有的,毕竟我这种人只会耍嘴炮=.=(这种模板题放了代码也没用吧,自己写一遍最好理解)


你可能感兴趣的:(BZOJ,主席树,分治,分块,平衡树,树套树,线段树)