題面
【題目描述】
給定一個序列,如果存在並且,那麼我們稱之爲逆序對,求逆序對的數目。
【輸入】
第一行爲,表示序列長度,接下來的行,第行表示序列中的第個數。
【輸出】
所有逆序對總數。
【樣例輸入】
4
3
2
3
2
【樣例輸出】
3
【數據範圍】
算法分析
歸併對可以使用歸併排序求解,在這裏我們使用樹狀數組來實現。
(1)定義數組,表示數值爲出現的次數,即桶計數。
再定義樹狀數組,表示數值在區間的個數。
(2)逆序訪問個數(),對於,統計前綴和,表示值範圍在 ~ 的個數,因爲逆序訪問,前綴和包含的數全部比小,且在後面,形成了逆序對個。
(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;
}