八.逆序对的数量
当前一个数比后一个数严格大(即不会相同)的时候,就称两个数为逆序对
也用到了分治的思想。首先看归并的基本思想:
1.将整个区间一分为二。[L,R]=>[L,mid],[mid+1,R]
2.递归处理两个子区间
3.归并两个序列
然后将所有可能存在的逆序对视为三大类。1.两个数同时出现在左区间2.同时出现在右半边3.一个左一个右。
接下来分情况讨论(假设归并的同时能统计三种情况,则逆序对数量为三种情况的数量之和:
1.第一种情况:左半边内部逆序对数量,其个数应为merge_sort(L,mid)
2.第二种情况:右半边……merge_sort(mid+1,R)
3.第三种情况:左右两个区间,首先得到右半边区间第一个数,接下来统计左边有多少个数大于它(即构成逆序对),记为S1。然后是右边第二个数……记为S2。直到第m个数记为Sm。
快速算出S的思路:用两个指针指向左右两个区间的数,左区间的往后挪,当某个数开始左区间的数比右区间指向的数大时,其之后的数必然比右区间目前所指向的数大(因为左右区间都已经排序好了)。假设此时为第j个数,则Sj为mid-i+1。
因此,当我们进行归并排序时,每次需要输出q[j]的时候,只需要在最终答案中加上一个mid-i+1即可,这就是第三种情况的数量统计。
//注意数据范围,当1<=n<=100000时逆序对数量可能会爆int,所以用long int存
#include<iostream>
using namespace std;
typedef long long LL;//偷懒而已
const int N=100010;
int n,q[N];
LL merge_sort(int l,int r){
if(l>=r) return 0;
int mid=l+r>>1;// >>符号优先级小于+所以不用加括号
LL res=merge_sort(l,mid)+merge_sort(mid+1,r);
//归并的过程
int k=0,i=l,j=mid+1;
int tmp[100010];
while(i<=mid&&j<=r){
if(q[i]<=q[j]) tmp[k++]=q[i++];
else {tmp[k++]=q[j++];res+=mid-i+1;}
}
//扫尾,左右一起扫尾
while(i<=mid) tmp[k++]=q[i++];
while(j<=r) tmp[k++]=q[j++];
//物归原主
for(int i=l,j=0;i<=r;i++,j++) q[i]=tmp[j];
return res;
}
int main(){
cin>>n;
for(int i=0;i<n;i++) cin>>q[i];
cout<<merge_sort(0,n-1)<<endl;
return 0;
}