题目大意:
有一个数列,问ai>aj同时i<j的元素对 在这个数列中有多少对
解题思路:
首先,暴力思路n^2,直接TLE,所以考虑更快的算法。
这里我们假设堆排从小到大。
在这里我们要使用堆排,为什么使用堆排呢,因为在堆排中,我们假设左右序列都已经排好序了,所以我们再找逆序对的时候,其实减少了重复工作,比如:现在左边序列是 5,6;右边序列是4,7。那么我们的流程是这样的:
i=j=0 //i和j分别是左右序列的指针
5>4
ans+=1 //ans 是我们的结果
j+=1 //右边序列指针加一
5<7
i+=1 //访问左边序列的下一个元素的意思
关键的地方是这时候,我们的ans+=j
为什么可以这样呢?因为j代表i前面的最大的元素大于右边序列的j个元素,所以每次i+=1,我们认为这个访问到的左边序列的新元素是大于j个元素的,因为左边序列已经进行了排序!ai已经是当前遍历的左序列最大的元素了
题外话:
堆排里面把n^2能够压缩为n log n,很重要的思想是,我们必须假设左右两段序列(堆排每次都会把序列分成两部分,除非这段只剩一个元素)已经排好序,进行指针操作插入到新序列,这时候我们的指针操作不再是n^2而是 n。比如,在堆排中,我们已经知道一部分排好序了,那么当我们遍历完左序列或者右序列插入一部分序列之后,剩下的左序列或者右序列元素可以直接插入到新序列里面。
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int MAXN=5e5+10;
int arr[MAXN];
int ans=0;
int ltemp[MAXN];
int rtemp[MAXN];
void merge(int l,int r){
int m=l+(r-l)/2;
int count=0;
for(int i=l;i<=m;i++){
ltemp[count++]=arr[i];}
count=0;
for(int i=m+1;i<=r;i++){
rtemp[count++]=arr[i];}
int lcount=m-l+1;
int rcount=r-(m+1)+1;
int i=0;int j=0;int cur=l;
while(i<lcount&&j<rcount){
if(ltemp[i]<=rtemp[j]){
arr[cur++]=ltemp[i];
i++;
if(i!=lcount)
ans+=j;
}else{
ans+=1;
arr[cur++]=rtemp[j];
j++;
}
}
if(j==rcount)ans+=(lcount-(i+1))*rcount;
for(;i<lcount;i++)arr[cur++]=ltemp[i];
for(;j<rcount;j++)arr[cur++]=rtemp[j];
assert(cur==r+1);
}
void merge_sort(int l,int r){
if(l==r)return;
int m=l+(r-l)/2;
merge_sort(l,m);
merge_sort(m+1,r);
merge(l,r);
}
int32_t main(){
// interesting
int n;;
cin>>n;
for(int i=0;i<n;i++){
cin>>arr[i];
}
merge_sort(0,n-1);
cout<<ans<<endl;
return 0;
}