【上海交大oj】逆序数对(归并排序)

Description

给你一个数组,求该数组的逆序数

Input Format

输入文件第一行包含一个自然数N,N个数 接下来有N行,表示a[0],a[1]...a[n - 1]

Output Format

输出文件仅有一行包含一个整数,表示该数组的逆序数

Sample Input

3

575085724

344369358

808884464

Sample Output

1
==============================================
所谓逆序数,就是一个数列中的元素,比它大并且排在它前面的数的个数,然后对每个元素逆序数求和就是答案。
如果直接做的话,对每个数都要扫描它前面的所有数,复杂度为n^2,因此可以用归并排序的思想。所谓归并排序,有点类似分治法,比如4,1,3,2,首先把序列分为两半并调用归并函数(递归直到只有一个元素)分别得到两个有序子序列,然后对这两个子序列进行归并.归并过程是这样的:
对两个子序列同时从左到右扫描,选取较小值加入序列。对于1,42,3,选取顺序是为:1->2->3->4.
那么怎么统计逆序数对呢?举个例子,当扫描到3时,对于它自身所在子序列:在它以前的元素肯定比它小,不计,在它后面的元素在因为在对这个子序列排序时已经计算过了(原来是3,2,子序列分别为3和2,那么扫描到2的时候会对3统计)也不用计。对于这个时刻和它进行比较的子序列:此时和它比较的元素以前的肯定比它小(如1,4中的元素4以前的元素(1)),不计,只要计算和它比较的元素本身及之后的元素就行了。
因此,等到排完序(nlogn)后,逆序数对也就求出来了。
具体代码:
 1 #include <iostream>

 2 using namespace std;

 3 long long n,count = 0;

 4 long long arr[100000],a[100000],b[100000];//要计算的序列和两个子序列 

 5 void mergesort(int , int);

 6 void merge(int, int, int);//归并过程 

 7 int main(){

 8     int n;

 9     

10     cin>>n;

11     for (int i = 0; i < n;++i) cin>>arr[i];

12     mergesort(0,n-1);

13     cout<<count;

14     

15     return 0;

16 }

17 void merge(int l,int m,int r){

18     if (l==r) return;

19     int len1 = m - l + 1,p = 0,q = 0;

20     int len2 = r - m;

21     for (int i = 0;i < len1;++i) a[i] = arr[i+l];

22     for (int i = 0;i < len2;++i) b[i] = arr[m+i+1];

23     a[len1] = 1000000000000000000;

24     b[len2] = 1000000000000000000;

25     for (int i = l;i <= r;++i){

26         if (a[p] <= b[q]) arr[i] = a[p++];

27         else{

28             count += m - p -l + 1;//计算逆序数对 

29             arr[i] = b[q++];

30         } 

31     }

32 }

33 void mergesort(int l,int r){

34     if (l>=r) return;

35     int m = (l+r)/2;

36     mergesort(l,m);

37     mergesort(m+1,r);

38     merge(l,m,r);

39 }
View Code

 



你可能感兴趣的:(归并排序)