逆序数
在一个排列中,如果一对数的前后位置与大小顺序相反,即前面的数大于后面的数,那么它们就称为一个逆序。一个排列中逆序的总数就称为这个排列的逆序数。
Input
第1行:N,N为序列的长度(n <= 50000)
第2 - N + 1行:序列中的元素(0 <= Aii <= 10^9)
Output
输出逆序数
Sample Input
4 2 4 3 1
Sample Output
4
逆序数的求法有两种,一种是树状数组,另一种就是利用分治法来求,我们这次只考虑用分治法来求逆序数。
很显然,对一个数组来说,我们选择这个这个数组的中点,假设这个数组为a,那么我们把它分隔成为了b,c两个数组
很显然,逆序对应该有以下三种情况:
扫描二维码关注公众号,回复:
2534189 查看本文章
1、存在于b数组的逆序对个数
2、存在于c数组的逆序对个数
3、存在于b,c组合在一起的逆序对个数
对于前两种,我们直接递归就可以得到答案,对于第三种,我们该怎么得到呢?
我们其实在整个递归的过程中是已经对b,c进行了排序的(没错,就是归并排序),也就是说在当前的状态下,b和c都是有序的
那么问题就简单了我们从b和c的第一项开始逐个进行比较,如果b[i]<=c[j],那么我们就把b放到原数组中,否则就把c[j]放到原数组中(我们是按照从小到大来排序的),在吧c[j]放到原数组的过程中,我们就开始统计逆序对的个数了:由于b和c是有序的,所以如果c[j]<=b[i],那么从b[i]以后的所有b中的元素都是大于c[j]的,因此逆序对的个数直接+=(b数组的大小-b[i]),就这样,我们得到了这次比较产生的逆序对的个数。同时也完成了归并排序,对于n个数,我们在整个过程中递归了log(n)次,每次合并的复杂度为O(n),因此整个的复杂度为O(n*log(n))。
#include<iostream>
#include<algorithm>
#include<vector>
#include<string.h>
using namespace std;
typedef long long ll;
vector<ll> a;
ll mergecount(vector<ll> &a)
{
ll n=a.size();
if(n<=1) return 0;
ll cnt=0;
vector<ll> b(a.begin(),a.begin()+n/2);
vector<ll> c(a.begin()+n/2,a.end());
cnt+=mergecount(b);//第一种逆序对
cnt+=mergecount(c);//第二种逆序对
ll ai=0,bi=0,ci=0;
while(ai<n){//第三种逆序对的求解以及O(n)合并
if(bi<b.size()&&(ci==c.size()||b[bi]<=c[ci])){
a[ai++]=b[bi++];
}
else{
cnt+=n/2-bi;
a[ai++]=c[ci++];
}
}
return cnt;
}
int main()
{
ll n,x;
while(cin>>n&&n){
a.clear();
for(ll i=0;i<n;i++){
cin>>x;
a.push_back(x);
}
cout<<mergecount(a)<<endl;//逆序对的个数
for(int i=0;i<a.size();i++) cout<<a[i]<<" ";//排序后的数组
}
return 0;
}