题目描述
给定一个长度为n的整数数列,请你计算数列中的逆序对的数量。
逆序对的定义如下:对于数列的第 i 个和第 j 个元素,如果满足 i < j 且 a[i] > a[j],则其为一个逆序对;否则不是。
输入格式
第一行包含整数n,表示数列的长度。
第二行包含 n 个整数,表示整个数列。
输出格式
输出一个整数,表示逆序对的个数。
数据范围
1≤n≤100000
输入样例:
6
2 3 4 5 6 1
输出样例:
5
思路
归并排序:
1.[L,R]=>[L,mid],[mid+1,R]
2.递归排序[L,mid]和[mid+1,R]
3.归并,将两个有序的序列合并成一个有序的序列
如何应用:
此题一共有三种情况:
①两个数同时出现在左半边:
merge_sort(L,mid)
②两个数同时出现在右半边:
merge_sort(mid+1,R)
③一个数在左半边,一个数在右半边
设左半边的数组为L[],右半边的数组为R[]
找大于R[]里的某一个数在L[]里有多少个数大于它,将这个数记录在s[]里,将每一个点都进行这样以此判断,最后的结果就是s[]里的每一个数之和
如果在L[]里有一个数q[i]大于R[]里正在找的那个数q[j],那么q[i]后面的数都大于q[j] (即q[i]前面的数都小于等于q[j])所以s[j]=mid-i+1(至于为什么要加1,我们可以举个栗子来看,比如mid=8,i=2,那么个数就是7个,即8-2+1)
数据范围(逆序对个数)
当整个数列是倒序的时候,逆序对数量是最多的
q[]={n,n-1,n-2……,1}
s[]={n-1,n-2,n-3……,1,0}
s[]每个数相加的和=(n(n-1))/2
所以大概就是(n^2)/2=>(10^10)/2=>5*10^9
注意:会爆int!
代码
#include<iostream>
using namespace std;
typedef long long LL;
const int N=100010;
int n;
int p[N],tmp[N];
LL merge_sort(int l,int r)
{
if(l>=r)
return 0;
int mid=(l+r)/2;
LL res=merge_sort(l,mid)+merge_sort(mid+1,r);
//递归左右两边的数组,将左右两边的数组情况,转化成第三种情况
//归并排序
int k=0,i=l,j=mid+1;
while(i<=mid&&j<=r)
{
if(p[i]<=p[j])
tmp[k++]=p[i++];
else
{
tmp[k++]=p[j++];
res+=mid-i+1;
}
}
//扫尾(对称扫)
while(i<=mid)
{
tmp[k++]=p[i++];
}
while(j<=r)
{
tmp[k++]=p[j++];
}
//物归原主
for(int i=l,j=0;i<=r;i++,j++)
{
p[i]=tmp[j];
}
return res;
}
int main()
{
cin>>n;
for(int i=0;i<n;i++)
{
cin>>p[i];
}
cout<<merge_sort(0,n-1);
return 0;
}
附:归并排序算法模板 —— 模板题 AcWing 787. 归并排序
void merge_sort(int q[], int l, int r)
{
if (l >= r) return;
int mid = l + r >> 1;
merge_sort(q, l, mid);
merge_sort(q, mid + 1, r);
int k = 0, i = l, j = mid + 1;
while (i <= mid && j <= r)
if (q[i] <= q[j]) tmp[k ++ ] = q[i ++ ];
else tmp[k ++ ] = q[j ++ ];
while (i <= mid) tmp[k ++ ] = q[i ++ ];
while (j <= r) tmp[k ++ ] = q[j ++ ];
for (i = l, j = 0; i <= r; i ++, j ++ ) q[i] = tmp[j];
}