1. 程式人生 > >BZOJ3295 CQOI2011 動態逆序對

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

輸入第一行包含兩個整數n
m,即初始元素的個數和刪除的元素個數。以下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大法好,離線大法好)


至於程式碼呢,我當然是沒有的,畢竟我這種人只會耍嘴炮=.=(這種模板題放了程式碼也沒用吧,自己寫一遍最好理解)