P1908 逆序对(归并排序)

Toretto·2021-12-04 12:22·14 次阅读

P1908 逆序对(归并排序)

题目描述:

题目传送门


解题思路:

对于这个问题,我们显然能通过冒泡或选择排序来累计交换次数达到所求目的。但很显然, Θ ( n 2 ) \Theta(n^2) Θ(n2) 的排序无法承担 5 ∗ 1 0 5 5*10^5 5105 的数据,因此我们需要用到 Θ ( n   l o g n ) \Theta(n~log_n) Θ(n logn) 的归并排序。

归并排序:

首先我们考虑要排序一段 ( l , r ) (l,r) (l,r) 的区间,归并排序需要考虑分裂和合并两个步骤,我们先看看分裂。
在这里插入图片描述

如图,对于一个 ( l , r ) (l,r) (l,r) 的区间,归并排序会将这个区间分成两个长度相等的左右区间 ( l , m i d ) (l,mid) (l,mid) ( m i d + 1 , r ) (mid+1,r) (mid+1,r),这样原问题就分解成了等价的两个子问题,然后子问题再进一步分裂,分成更小的问题,知道分成每个区间只有一个元素 a i a_i ai,为止。此时,当区间长度为 1 1 1,我们发现区间本身已经是有序的了,因此我们要把这种子区间的有序带到父区间中,也就是合并。

将左右各自有序的区间合并成跟大的区间,这样的话父区间也将有序,然后继续合并,等最后的根区间合并完了,那么 ( l , r ) (l,r) (l,r) 区间就变得有序了。

合并步骤 Θ ( n ) \Theta(n) Θ(n),分裂是 Θ ( l o g n ) \Theta(log_n) Θ(logn),归并排序的复杂度就是 Θ ( n   l o g n ) \Theta(n~log_n) Θ(n logn)

这里有必要讲讲 Θ ( n ) \Theta(n) Θ(n) 合并的过程,如图:
在这里插入图片描述
我们需要一个一开始 指向区间 A A A 头部的指针 i i i ,还需要一个指向区间 B B B 头部的指针 j j j,开始考虑比较 a i a_i ai 以及 b j b_j bj,若 a i > b j a_i>b_j ai>bj 则将 b j b_j bj 插入 i i i 前,也就是 i − 1 i-1 i1 的位置,并将 j j j 指针往后移动一位;若 a i < b j a_i<b_j ai<bj 则将 i i i 指针往后移动一位, 以此往复,区间将成功有序合并。

在程序中我们需要额外的一个数组作为空间存放这个合并后的区间,最后还得把这个数组重新放入区间 A A A B B B

以下是完整的归并排序代码:

int t[100010]={0}; //额外空间
void msort(int l,int r)
{
	if(l-r==0) return ;
	int mid;
	mid=l+(r-l)/2;
	msort(l,mid);  //左区间排序
	msort(mid+1,r);  //右区间排序
	int i=l,j=mid+1,k=l;
	while(i<=mid&&j<=r)
	  {
	  	if(a[i]>a[j])
	  	  {
	  	  	t[k]=a[j];
	  	  	j++;
	  	  	k++;
		  }
		else
		  {
		  	t[k]=a[i];
		  	i++;
		  	k++;
		  }
	  }
	while(i<=mid)  //若某一段区间插入完毕了,剩下的直接放进去就行了
	  {
	  	t[k]=a[i];
	  	i++;
	  	k++;
	  }
	while(j<=r)
	  {
	  	t[k]=a[j];
	  	j++;
	  	k++;
	  }
	for(i=l;i<=r;i++)
	  a[i]=t[i];
	return ;
}

那么对于题目中的逆序对个数问题,我们可以找到一些合并过程中 B B B 区间的数小于 A A A 区间的数的数对 ,简单推推就能发现,一个包含 a i a_i ai 的这样的数对就能带来 m i d − i + 1 mid-i+1 midi+1 个逆序对。原因也很简单,由于 A A A 区间中的数是有小到大有序排列的,可知,若 a i > b j a_i>b_j ai>bj i i i以后的所有 a a a 也都必定大于 b j b_j bj ,即都是逆序对,个数为 m i d − i + 1 mid-i+1 midi+1


CODE:

#include <bits/stdc++.h>
using namespace std;
int n,a[500010]={0},t[500010]={0};
long long ans=0;
void msort(int l,int r)
{
	if(l-r==0) return ;
	int mid;
	mid=l+(r-l)/2;
	msort(l,mid);
	msort(mid+1,r);
	int i=l,j=mid+1,k=l;
	while(i<=mid&&j<=r)
	  {
	  	if(a[i]>a[j])
	  	  {
	  	  	t[k]=a[j];
	  	  	j++;
	  	  	k++;
	  	  	ans+=(mid-i+1);  //统计逆序对
		  }
		else
		  {
		  	t[k]=a[i];
		  	i++;
		  	k++;
		  }
	  }
	while(i<=mid)
	  {
	  	t[k]=a[i];
	  	i++;
	  	k++;
	  }
	while(j<=r)
	  {
	  	t[k]=a[j];
	  	j++;
	  	k++;
	  }
	for(i=l;i<=r;i++)
	  a[i]=t[i];
	return ;
}
int main()
{
	cin>>n;
	for(int i=1;i<=n;i++)
	  cin>>a[i];
	msort(1,n);
	cout<<ans;
	return 0;
}
posted @   S·A·I  阅读(14)  评论(0编辑  收藏  举报  
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!
点击右上角即可分享
微信分享提示