题目描述
貌似出现了无法跳转的情况:https://www.acwing.com/problem/content/790/
acwing官网的题库中有,可以去找找。
给定一个长度为 n n n的整数数列,请你计算数列中的逆序对的数量。
逆序对的定义如下:对于数列的第 i i i个和第 j j j个元素,如果满足 i < j i<j i<j且 a [ i ] > a [ j ] a[i]>a[j] a[i]>a[j],则其为一个逆序对;否则不是。
输入格式
第一行包含整数 n n n,表示数列的长度。
第二行包含 n n n个整数,表示整个数列。
输出格式
输出一个整数,表示逆序对的个数。
数据范围
1
≤
n
≤
100000
1\leq{n}\leq{100000}
1≤n≤100000,
数列中的元素的取值范围
[
1
,
1
0
9
]
[1,10^9]
[1,109]。
输入样例:
6
2 3 4 5 6 1
输出样例:
5
解题思路与代码
一般看到这个题,第一想到的肯定是暴力比较,然后计算逆序对的个数,一方面这个时间复杂度为 O ( n 2 ) O(n^2) O(n2),另一方面,这个过程中只能进行计数,不能存储一些有用的信息。
其实可以想一下归并排序,归并排序的过程是:把一个数组不断的划分成两个数组,利用dfs的思想,划分到最底层,也就是数组中只有一个元素的时候,这个时候数组肯定是有序的,之后就可以进行合并了。合并的过程是从底部向上进行合并的。我们就可以利用归并排序中“并”的过程计算逆序对的个数。
在比较的过程中:
- a r r [ j ] < a r r [ i ] : arr[j]<arr[i]: arr[j]<arr[i]: 那么前面的有序数组的剩余的个数, a r r [ j ] arr[j] arr[j]的逆序对个数; [ l e f t , m i d ] [left,mid] [left,mid], n u m = m i d − i + 1 num=mid-i+1 num=mid−i+1;
- a r r [ j ] > a r r [ i ] : arr[j]>arr[i]: arr[j]>arr[i]: 逆序对的个数肯定为0,首先在前面的有序数组中, a r r [ i ] arr[i] arr[i]后面的肯定都比 a r r [ i ] arr[i] arr[i]大,其次, a r r [ j ] arr[j] arr[j]已经大于 a r r [ i ] arr[i] arr[i]了,那么 a r r [ j ] arr[j] arr[j]之后的元素肯定也是大于 a r r [ i ] arr[i] arr[i]的,所以, a r r [ i ] arr[i] arr[i]之后的元素肯定都比 a r r [ i ] arr[i] arr[i]大,所以不存在逆序对。
AC代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int arr[100010];
int temp[100010];
ll num = 0;
void merge(int left, int mid, int right){
int i = left;
int j = mid + 1;
int count = 0;
while(i<=mid && j<=right){
if(arr[i]<=arr[j]){
temp[count++] = arr[i++];
}else{
//只需要在归并排序的基础上,加这一行代码就够了
num += (mid - i + 1);
temp[count++] = arr[j++];
}
}
while(i<=mid){
temp[count++] = arr[i++];
}
while(j<=right){
temp[count++] = arr[j++];
}
count = 0;
while(left<=right){
arr[left++] = temp[count++];
}
}
void mergeSort(int left, int right){
if(left >= right) return;
int mid = left + (right - left)/2;
mergeSort(left, mid);
mergeSort(mid+1, right);
merge(left, mid, right);
}
int main(){
int n;
cin>>n;
for(int i=0; i<n; i++){
cin>>arr[i];
}
mergeSort(0, n-1);
cout<<num<<endl;
return 0;
}