POJ_2299_Ultra-quicksort_归并排序、逆序数

这题是好多天前做的,不知道怎么回事没有写题解。


题意:

给你一个神秘的排序,其实就是冒泡排序,给一个序列,问用这个神秘的排序要进行多少词swap操作。

Input

The input contains several test cases. Every test case begins with a line that contains a single integer n < 500,000 -- the length of the input sequence. Each of the the following n lines contains a single integer 0 ≤ a[i] ≤ 999,999,999, the i-th input sequence element. Input is terminated by a sequence of length n = 0. This sequence must not be processed.

Output

For every input sequence, your program prints a single line containing an integer number op, the minimum number of swap operations necessary to sort the given input sequence.
最朴素的想法就是冒泡排序,加一个变量记录swap次数。但是冒泡O(n^2)的复杂度,对5*10^5显然是不行的。

这个题要用一个数学知识,就是线性代数里的逆序数,然后TM我们学校学的那本外文的线代提都没提啊哦凑!!

对一个数列只通过交换相邻的两个数,从而把这个序列调整为有序所需要的步骤(这就是答案啊哦凑),等于这个序列所有数的逆序数之和。

逆序数就是在这个数后面的,但是应该排在这个数前面的数的个数。

数学知识说完了,那么就可以把答案转化为求序列逆序数和,就可以用别的排序来做,在排序的同时,统计逆序数,所以选择比较快的两个O(nlogn)排序算法之一的我还记得该怎么写的归并排序。

代码如下:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<cmath>
using namespace std;
#define mxn 500010
int n;
int a[mxn];
long long sum;
long long merge(int l,int r){
	int m=(l+r)/2;
	int tem[mxn];
	int ls=l,rs=m+1,cnt=0;
	long long ret=0;
	while(ls!=m+1||rs!=r+1){
		if(ls==m+1)	tem[cnt++]=a[rs++];
		else if(rs==r+1)	tem[cnt++]=a[ls++];
		else if(a[ls]<a[rs])	tem[cnt++]=a[ls++];
		else{
			tem[cnt++]=a[rs++];
			ret+=m-ls+1;
		}
	}
	for(int i=l;i<=r;++i)	a[i]=tem[i-l];
	return ret;
}
long long merge_sort(int l,int r){
	if(l==r)	return 0;
	int m=(l+r)/2;
	long long ret=0;
	ret+=merge_sort(l,m)+merge_sort(m+1,r);
	ret+=merge(l,r);
	return ret;
}
int main(){
	sum=0;
	while(scanf("%d",&n)!=EOF&&n){
		for(int i=0;i<n;++i)	scanf("%d",&a[i]);
		long long ans=merge_sort(0,n-1);
		printf("%lld\n",ans);
	}
	return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值