【題解】逆序對(樹狀數組)

題面

【題目描述】
給定一個序列a1,a2,...,ana_1,a_2,...,a_n,如果存在i<ji < j並且ai>aja_i>a_j,那麼我們稱之爲逆序對,求逆序對的數目。
【輸入】
第一行爲nn,表示序列長度,接下來的nn行,第n+1n+1行表示序列中的第ii個數。
【輸出】
所有逆序對總數。
【樣例輸入】

4
3
2
3
2

【樣例輸出】

3

【數據範圍】
n<=105,ai<=105n<=10^5,a_i<=10^5。

算法分析

歸併對可以使用歸併排序求解,在這裏我們使用樹狀數組來實現。
(1)定義數組sss[x]s[x]表示數值爲xx出現的次數,即桶計數。
再定義樹狀數組ccc[x]c[x]表示數值在區間[xlobit(x)+1,x][x-lobit(x)+1,x]的個數。
(2)逆序訪問nn個數(a[n],a[n1],...a[1]a[n],a[n-1] ,...a[1]),對於a[i]a[i],統計前綴和sum(i1)sum(i-1),表示值範圍在11 ~ i1i-1的個數,因爲逆序訪問,前綴和包含的數全部比a[i]a[i]小,且在a[i]a[i]後面,形成了逆序對sum[i1]sum[i-1]個。
(3)將每次前綴和相加,就是最後的答案。
(4)訪問完a[i],就執行單點增加,數值爲a[i]的個數+1,即s[a[i]]+1。
如果數值太大,桶裝不下怎麼辦呢?可以使用離散化,所謂離散化就是把無限空間中有限的個體映射到有限的空間中去,以此提高算法的時空效率。通俗的說,離散化是在不改變數據相對大小的條件下,對數據進行相應的縮小。
例如:我需要求解序列:99999999 199999999 88888888的逆序對,實際可以看作是求序列:1 3 2的逆序對,因爲逆序對跟大小關係有關,和具體的值無關。
【程序實現】:
這裏的s數組,可以不使用,方便大家理解。

#include<bits/stdc++.h>
#define N 500100
using namespace std;
int n,c[N],a[N],maxn;
int lowbit(int x)
{
    return x&(-x);
}
void update(int x,int val)
{
    for(int i=x;i<=maxn;i+=lowbit(i))	
        c[i]+=val;
}
int sum(int x)
{
    int ans=0;
   for(int i=x;i>0;i-=lowbit(i))
        ans+=c[i];
    return ans;
}
int main()
{
    scanf("%d",&n);
    long long ans=0;
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&a[i]);
        maxn=max(maxn,a[i]);	//最大值 
    }
    for(int i=n;i>=1;i--)
    {
        ans+=(long long)sum(a[i]-1);
        update(a[i],1);
    }
    cout<<ans<<endl;
    return 0;
 } 
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章