逆序对

题目描述

对于一个包含N个非负整数的数组A[1…n],如果有i < j,且A[ i ]>A[ j ],则称(A[ i] ,A[ j] )为数组A中的一个逆序对。   例如,数组(3,1,4,5,2)的逆序对有(3,1),(3,2),(4,2),(5,2),共4个。

求n个数中的逆序对个数。

输入

第一行一个整数n(<=1,000,000)
第二行n个整数,a[i]<=2^31-1

输出

n个数中逆序对个数

样例输入

5
3 1 4 5 2

样例输出

4

1.我们可以使用归并排序

为什么使用递归?

answer:要使用归并排序首先就要将数据分解,一直分解到每一个单位,然后就是进行合并了;

如何合并?

answer:比较ai,aj的大小。其中ai 属于左区间,aj属于右区间,其实就是将左右区间合并、并排序),若ai<aj ,则将ai复制到r[k]中,然后将i和k都加1,否则将aj
复制到r[k]中,将j,k加1,最后再将r[k]移动到ai中,然后继续合并;

下面归并排序的逆序对写法:

#include<bits/stdc++.h>
using namespace std;
int n;
long long ans;
int arr[1000005];
int arr1[1000005];
void Merge(int l,int r,int mid){
	int i=l;
	int j=mid+1;
	int k=l;
	while(i<=mid&&j<=r){
		if(arr[i]<=arr[j])
			arr1[k++]=arr[i++];
		else
			arr1[k++]=arr[j++],ans+=mid-i+1;
	}
	if(i<=mid)
		while(i<=mid)
			arr1[k++]=arr[i++];
	else
		while(j<=r)
			arr1[k++]=arr[j++];
	for(int m=l;m<=r;m++)
		arr[m]=arr1[m];
}
void Merge_sort(int l,int r){
	if(l<r){
		int mid=l+(r-l)/2;
		Merge_sort(l,mid);
		Merge_sort(mid+1,r);
		Merge(l,r,mid);
	}
}
int main(){
	scanf("%d",&n);
	for(int i=1;i<=n;i++)
		scanf("%d",&arr[i]);
	Merge_sort(1,n);
	printf("%lld",ans);
	return 0;
}

2.线段树求法:
在这里,我们需要先离散化一下,然后到着插入线段树(n-1),每次在线段树中查找比他小的值有几个(ans加上他),再将当前a[i]的值在线段树上加一,最后输出就行了

代码:

#include<bits/stdc++.h>
using namespace std;
int n;
long long ans;
long long b[1000005];
long long a[1000005];
long long tree[4000005];
struct node{
	long long val;
	int id;
}x[1000005];
int query(int p,int l,int r,int x,int y){
	if(l==x&&r==y)
		return tree[p];
	int mid=l+(r-l)/2;
	if(y<=mid)
		return query(p+p,l,mid,x,y);
	else if(x>mid)
			return query(p+p+1,mid+1,r,x,y);
		 else
		 	return query(p+p,l,mid,x,mid)+query(p+p+1,mid+1,r,mid+1,y);
}
void Insert(int p,int l,int r,int x,int val){
	if(l==r){
		tree[p]+=val;
		return;
	}
	int mid=l+(r-l)/2;
	if(x<=mid)
		Insert(p+p,l,mid,x,val);
	else
		Insert(p+p+1,mid+1,r,x,val);
	tree[p]=tree[p+p]+tree[p+p+1];
}
bool cmp(node a,node b){
	return a.val<b.val;
}
int main(){
	scanf("%d",&n);
	for(int i=1;i<=n;i++){
		scanf("%lld",&a[i]);
		x[i].val=a[i];
		x[i].id=i;
	}
	sort(x+1,x+n+1,cmp);		//离散化
	b[1]=1;
	x[0].val=983478348;
	for(int i=2;i<=n;i++){
		if(x[i].val==x[i-1].val)	
			b[i]=b[i-1];
		else
			b[i]=b[i-1]+1;
	}
	for(int i=1;i<=n;i++)
		a[x[i].id]=b[i];
	for(int i=n;i>=1;i--){
		ans+=query(1,0,n,0,a[i]-1);
		Insert(1,0,n,a[i],1);
	}
	printf("%lld",ans);
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值