P1908 逆序对 (归并排序)题解

【题目链接】

link

【解题思路】

对于这道题我们很容易就能想出 Θ ( n 2 ) \Theta(n^2) Θ(n2) 的冒泡做法

for (int i=1;i<n;i++)
	for (int j=1;j<=n-i;j++)
		if (a[i]>a[j])
		{
			ans++;
			swap(a[i],a[j]);
		 } 

但是题目中说了
对于所有数据, n ≤ 5 × 1 0 5 n \leq 5 \times 10^5 n5×105

Θ ( n 2 ) = 1 0 5 × 1 0 5 = 1 0 10 \Theta(n^2)=10^5 \times10^5=10^{10} Θ(n2)=105×105=1010 很显然他会TLE。
我们需要用到另一种排序,归并排序,我们先来了解一下归并的过程,其实就是先分再合。

分的步骤就是每一次都将这个区间 / 2 /2 /2 分成两个区间就可以了,把有 n n n 个元素的区间分成 n n n 个含有 1 1 1 个元素的区间,时间复杂度为 Θ ( l o g n ) \Theta(logn) Θ(logn)

P1908 逆序对 (归并排序)题解_第1张图片
合的步骤就是用两个指针扫描两个区间比较元素的大小合并成新的区间
如果 a i < b j a_iai<bj
a i a_i ai 放入区间 c c c
否则
b i b_i bi 放入区间 c c c
当一个指针走完了时,我们只要把剩下的数全部丢进区间 c c c 即可,
这样我们就成功把两个区间合并成了一个区间。
时间复杂度为 Θ ( n ) \Theta(n) Θ(n)
P1908 逆序对 (归并排序)题解_第2张图片
P1908 逆序对 (归并排序)题解_第3张图片
把这两个操作合起来,我们就知道了归并排序的时间复杂度为 Θ ( n l o g n ) \Theta(nlogn) Θ(nlogn)
跑题目这 1 0 5 10^5 105 绰绰有余。
那么我们应如何计算题目要求的逆序对呢
我们发现如果 a i > b j a_i>b_j ai>bj,则 a i a_i ai b i b_i bi 为逆序对,并且因为 a a a 区间是有序的,后面的元素一定比前面的元素大,所以我们可以直接把它们全部直接累加。
s u m + = 区 间 a 的 元 素 总 数 − i + 1 sum+=区间a的元素总数-i+1 sum+=ai+1
这样子我们就能在归并排序的同时,求出逆序对。

【CODE】

#include
using namespace std;
long long n,ans=0;
int a[500100];
int t[500100];
void msort(int l,int r)
{
	if (l==r)return;
	int mid=l+(r-l)/2;
	msort(l,mid);//分成两个区间
	msort(mid+1,r);
	int i=l,j=mid+1,k=l-1;
	while (i<=mid&&j<=r)
	{
		if (a[i]<=a[j])//如果a[i]<=a[j],把a[i]丢进新区间t
		{
			t[++k]=a[i];
			i++;
		}
		else //否则把a[j]丢进去,并且累加逆序对的个数
		{
			t[++k]=a[j];
			j++;
			ans+=(mid-i+1);
		}
	}
	while (i<=mid)//把a区间剩下未放完的元素放入
		{
			t[++k]=a[i];
			i++;
		}
	while (j<=r)//把b区间剩下未放完的元素放入
		{
			t[++k]=a[j];
			j++;
		}
	for (int p=l;p<=r;p++)//替换区间
		a[p]=t[p];
}
int main()
{
	cin>>n; 
	for (int i=1;i<=n;i++)
		cin>>a[i];
	msort(1,n);//把这一整个大的区间丢进去
	cout<<ans;//逆序对的个数
	return 0;
 } 

你可能感兴趣的:(板子,排序算法,算法,c++)