<font size=5>$$傳送門qwq$$</font>html
<font size=3>貓貓$TOM$和小老鼠$JERRY$最近又較量上了,可是畢竟都是成年人,他們已經不喜歡再玩那種你追我趕的遊戲,如今他們喜歡玩統計。</font>ios
<font size=3>最近,$TOM$老貓查閱到一我的類稱之爲「逆序對」的東西,這東西是這樣定義的:</font>c++
<font size=3>對於給定的一段正整數序列,逆序對就是序列中$a_i$>$a_j$且$i$<$j$的有序對。</font>git
<font size=3>知道這概念後,他們就比賽誰先算出給定的一段正整數序列中逆序對的數目。</font>算法
<font size=3>Update:數據已增強。</font>數組
<font size=3>第一行,一個數$n$,表示序列中有$n$個數。</font>ide
<font size=3>第二行$n$個數,表示給定的序列。序列中每一個數字不超過$10^9$</font>學習
<font size=3>給定序列中逆序對的數目。</font>spa
<font size=5>本蒟蒻今天學習了樹狀數組,今天來更新啦</font>code
<font size=3>逆序對是什麼東西呢,在題目已經給出介紹了</font>
<font size=3>對於給定的一段正整數序列,逆序對就是序列中$a_i$>$a_j$且$i$<$j$的有序對。</font>
<font size=3>好比你有一個$a$數組,若是在這個數組中中$a[i]$>$a[j]$而且$i$<$j$,咱們就稱它是一個逆序對</font>
<font size=3>這個題就是要求咱們求出輸入的數中逆序對的數量</font>
<font size=3>求逆序對的方法有不少種,能夠用樹狀數組,也能夠用線段樹,但因爲本蒟蒻沒有學過這倆玩意兒,因此只會用歸併排序,那麼歸併排序又是什麼呢</font>
<font size=3>歸併排序(MERGE-SORT)是創建在歸併操做上的一種有效的排序算法,該算法是採用分治法(Divide and Conquer)的一個很是典型的應用。將已有序的子序列合併,獲得徹底有序的序列;即先使每一個子序列有序,再使子序列段間有序。若將兩個有序表合併成一個有序表,稱爲二路歸併。</font>
<font size=3>能夠來這裏理解一下什麼是歸併排序</font>
<font size=3>那麼歸併排序有什麼優勢呢?</font>
<font size=3>* 歸併排序是利用歸併的思想實現的排序方法,該算法採用經典的分治策略</font> <font size=3>* 歸併排序是穩定排序</font> <font size=3>* 歸併排序的最好,最壞,平均時間複雜度均爲O($nlogn$)。</font>
<font size=3>如何實現求逆序對??</font>
<font size=3>假設咱們手頭有兩個已經從小到大排好序的數組,且他們分別是原來的一段大數組的前半段和後半段,如今咱們的比較到了X,Y
位置</font>
<font size=3>假使X>Y
則其必然構成一對逆序對,與此同時我所畫出的紅色的這一段的的數均大於Y,因此逆序對的數量就要加上這一段的長度</font>
<font size=3>而對整個數組都歸併排序完畢後最終就能夠獲得逆序對的和</font>
#include<cstdio> #include<iostream> #include<cmath> using namespace std; int n; long long ans; int a[5000007],b[5000007]; void sort(int l,int r) { if(l==r)return; int m=(l+r)>>1; sort(l,m); sort(m+1,r); int i=l,j=m+1,k=l; while(i<=m&&j<=r) { if(a[i]<=a[j])b[k++]=a[i++]; else ans+=m-i+1,b[k++]=a[j++]; } while(i<=m)b[k++]=a[i++]; while(j<=r)b[k++]=a[j++]; for(i=l; i<=r; ++i)a[i]=b[i]; } inline int read() { int s=0,w=1; char ch=getchar(); while(ch<'0'||ch>'9') { if(ch=='-')w=-1; ch=getchar(); } while(ch>='0'&&ch<='9') { s=s*10+(ch-'0'); ch=getchar(); } return s*w; } int main() { scanf("%d",&n); for(int i=1; i<=n; i++) { a[i]=read(); } sort(1,n); printf("%lld",ans); return 0; }
##思路2
<font size=3>咱們能夠用樹狀數組實現</font>
<font size=3>離散化一下,直接將輸入的數變爲負數,在以後按價值從小到大排序,排完序以後再用樹狀數組維護,並每次把這個數的位置加入到樹狀數組中</font>
<font size=3>由於是排完序以後,因此以前加入的必定比後加入的大,而後在查詢當前這個數前面位置的數(是前面位置的數,要當前這個數減1),就是逆序對的個數了</font>
##代碼
#include<bits/stdc++.h> #define N 500110 #define lowbit(i) i&-i using namespace std; int n,a[N],b[N],t[N]; long long ans=0; inline int read(){ int x=0,f=1;char c=getchar(); for(;!isdigit(c);c=getchar())if(c=='-')f=-1; for(;isdigit(c);c=getchar())x=x*10+c-48; return x*f; } inline void insert(int x){ for(int i=x;i<=n;i+=lowbit(i)){ t[i]++; } } inline int find(int x){ int ans=0; for(int i=x;i;i-=lowbit(i)){ ans+=t[i]; } return ans; } int main(){ n=read(); for(int i=1;i<=n;i++)a[i]=b[i]=-read(); sort(b+1,b+1+n); for(int i=1;i<=n;i++){ a[i]=lower_bound(b+1,b+n+1,a[i])-b; } for(int i=1;i<=n;i++){ ans+=find(a[i]-1); insert(a[i]); } cout<<ans<<'\n'; return 0; }