运用了分治的思想,将一个数组分成几乎相等的两份,分别将两段中第一个最小的数拿出来放在一个临时数组中,直到全部取完。因为是递归的,所以每一段的数列都是排序好的。
void merge_sort(ll *A,ll *B,int x,int y) { if(y-x<=1)return; int m=x+(y-x>>1); int p=x,q=m,i=x; merge_sort(A,B,x,m); merge_sort(A,B,m,y); while(p<m or q<y) { if(q>=y or (p<m and A[p]<=A[q]))B[i++]=A[p++]; else B[i++]=A[q++]; } for(int i=x;i<y;i++)A[i]=B[i]; }
(没验证,有错误请指出)
这段代码中左端为闭,右端为开。因此调用时要将右端点+1
O(nlogn)
它有一个神奇的应用:求逆序对(的和)!因为我们发现每次递归时左边的还没有入列的数都是大于右边的数的,所以我们只要在else句的后面加上ans+=m-p就好辣!
#include<iostream> #include<cstdio> using namespace std; typedef long long ll; template <typename T> inline T read() { int w=0;T x=0;char c=getchar(); while(!isdigit(c))w|=c=='-',c=getchar(); while(isdigit(c))x=(x<<3)+(x<<1)+(c^48),c=getchar(); return w?-x:x; } const int maxn=5e5+10; int n; ll A[maxn],B[maxn],ans; void merge_sort(ll *A,ll *B,int x,int y) { if(y-x<=1)return; int mid=x+(y-x)/2; int p=x,q=mid,i=x; merge_sort(A,B,x,mid); merge_sort(A,B,mid,y); while(p<mid or q<y) { if(q>=y or (p<mid and A[p]<=A[q]))B[i++]=A[p++]; else B[i++]=A[q++],ans+=mid-p; } for(int i=x;i<y;i++)A[i]=B[i]; } int main() { n=read<int>(); for(int i=1;i<=n;i++)A[i]=read<ll>(); merge_sort(A,B,1,n+1); printf("%lld ",ans); return 0; }