Sorry, your browser cannot access this site
This page requires browser support (enable) JavaScript
Learn more >

算法描述

运用了分治的思想,将一个数组分成几乎相等的两份,分别将两段中第一个最小的数拿出来放在一个临时数组中,直到全部取完。因为是递归的,所以每一段的数列都是排序好的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
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];
}

时间复杂度为 \(O(n\log n)\)

例题

可以利用分治的过程处理一些问题,比如求数组的逆序对。

我们发现每次递归时左边的还没有入列的数都是大于右边的数的,此时统计左侧比右侧该数大的数的个数,即可统计到所有的逆序对。

洛谷P1908

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
#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\n",ans);
return 0;
}

给小狼留言