逆序对的定义:序列中 i<j且 ai>aj 的有序对。
首先看一下样例:
A:5 4 2 6 3 1
那么数组A存在的逆序对分别是:
5 4
5 2
5 3
5 1
4 2
4 3
4 1
2 1
6 3
6 1
3 1
ans=11
我们通过打表可以发现,我们去固定一个左指针,右指针去遍历整个数组,寻找满足条件的逆序对的另一半,这也是最朴素的做法:
#include<bits/stdc++.h>
using namespace std;
#define int long long
int ans;
signed main()
{
cin.tie(nullptr)->sync_with_stdio(false);
int n;cin>>n;
vector<int>a(n);
for(int i=0;i<n;i++)
{
cin>>a[i];
}
for(int i=0;i<n-1;i++)
{
for(int j=i+1;j<n;j++)
{
if(a[i]>a[j])ans++;
}
}
cout<<ans<<endl;
return 0;
}
n的最大范围是5e10,朴素做法两层循环,一定会超时:
那么排序呢?排成降序,那么a[i]一定大于a[j],这样就不用一个一个去对比大小了,提高效率:
但是实际上排完序之后答案就不对了:
验证:
排完序之后的数组是:
A:6 5 4 3 2 1
那么数组A中存在的逆序对分别为:
6 5
6 4
6 3
6 2
6 1
5 4
5 3
5 2
5 1
4 3
4 2
4 1
3 2
3 1
2 1
ans=15
正解
一.归并排序
对于逆序对,我们这里采用归并排序分治思想:
比如,对于如下序列B,有多少对逆序对呢?
B:5 6 7 1 3 8
我们首先把它分为两个区间: 因为 5大于1,又因为左区间是升序区间,所以5之后的所有数都大于1
左指针:i
左区间:5 6 7
右区间:1 3 8
右指针:j
ans=3
因为5大于3,所以5之后的所有数都大于3
左指针:i
左区间:5 6 7
右区间:1 3 8
右指针: j
ans=6
5小于8,移动i指针,发现左区间的所有数都小于8
左指针: i
左区间:5 6 7
右区间:1 3 8
右指针: j
ans=6
那么数组B的逆序对个数就是6。
这种方法每个区间只需要遍历 n/2,总共需要遍历n ,分区间可以用二分去分,那么总共时间复杂度就是
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define M 500005
int a[M], temp[M], n;
long long ans = 0;
void msort(int l, int r)
{
//递归结束条件
if (l == r)return;
int mid = l + r >> 1;
msort(l, mid); //左区间排序
msort(mid + 1, r); //右区间排序
int i = l, j = mid+1, k = l;
while (i <= mid && j <= r)
{
//谁小谁给temp
if (a[i] <= a[j])temp[k++] = a[i++];
else
{
temp[k++] = a[j++];
ans+=mid - i + 1; //求逆序对的个数
}
}
//谁还剩,剩下的全部放到temp里面
while (i <= mid)temp[k++] = a[i++];
while (j <= r)temp[k++] = a[j++];
//放回原数组
for (int i = l; i <= r; i++)a[i] = temp[i];
}
signed main()
{
cin.tie(nullptr)->sync_with_stdio(false);
cin >> n;
for (int i = 1; i <= n; i++)cin >> a[i];
msort(1, n);
cout << ans << endl;
return 0;
}