1. 程式人生 > >淺談樹狀數組求逆序對

淺談樹狀數組求逆序對

nlog 不變 for i++ 需要 逆序對 date 縮小 math

做了一道樹上求逆序對的題,主要難點並不在於樹形結構,而是求逆序對數。(在我看來是這樣的)。

to洛谷P3605晉升者計數。

發現自己樹狀數組求逆序對還有個坑,先填上再說。再加上最近學的樹狀數組離散化,捋一捋思路。

首先是離散化

for(int i=1;i<=n;i++){
    a[i].v=read();
    a[i].id=i;
}
sort(a+1,a+1+n);按v排序
for(int i=1;i<=n;i++)b[a[i].id]=i;

在上述代碼中,首先我們輸入的是a[i].v,也就是一開始的數據,我們將其放到結構體裏,再記錄一下id,也就是原序。之後我們將a按照v排序,那麽得到的就是從小到大的順序。

又因為離散化是為了避免數據太大而出鍋,所以我們在乎的就是數據的相對順序,所以很容易地想到,將他們的順序作為新的權值,這樣就可以保證相對大小不變並且還可以最大化的縮小數據範圍。

那麽我們開一個新的數組,對應為離散化之後的數組,那麽這個數組一定是要和原數組的順序相同的,所以這個時候原來記錄的id也就是原序就有了用場,然後再把當前這個元素的排名也就是i賦給離散化數組的第a[i].id位即可。

再來說逆序對數,在一個序列中,如果存在
\[ i<j並且a[i]>a[j] \]
那麽就代表有一個逆序對。

考慮如何用樹狀數組實現\(nlogn\)的時間復雜度求逆序對數。

首先,如果數據大我們需要離散化。

然後從1到n枚舉,考慮當前枚舉到的位置i,他對總的逆序對的個數的貢獻就是他前面的權值大於他的權值的數的個數,所以我們在i的權值的位置修改樹狀數組,表示i的權值這個數已經出現過了,那麽到了後面就可以直接查詢從1到i的權值的位置的總和,因為1代表有,0代表沒有,所以返回的值即為從一到i小於等於他的權值的數有幾個,單步容斥即可得到大於他的數目也就是逆序對數。

code

for(int i=1;i<=n;i++){
    update(b[i],1);
    ans+=query(b[i]);
}

淺談樹狀數組求逆序對