这题是好多天前做的,不知道怎么回事没有写题解。
题意:
给你一个神秘的排序,其实就是冒泡排序,给一个序列,问用这个神秘的排序要进行多少词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;
}