淺談逆序對

<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

思路1

<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>

qwq

<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;
}
相關文章
相關標籤/搜索