P1908 逆序对

题目描述

猫猫TOM和小老鼠JERRY最近又较量上了,但是毕竟都是成年人,他们已经不喜欢再玩那种你追我赶的游戏,现在他们喜欢玩统计。最近,TOM老猫查阅到一个人类称之为“逆序对”的东西,这东西是这样定义的:对于给定的一段正整数序列,逆序对就是序列中ai>aj且i<j的有序对。知道这概念后,他们就比赛谁先算出给定的一段正整数序列中逆序对的数目。
Update:数据已加强。

输入输出格式

输入格式:

第一行,一个数n,表示序列中有n个数。

第二行n个数,表示给定的序列。序列中每个数字不超过10910^9109

输出格式:

给定序列中逆序对的数目。

输入输出样例

输入样例#1: 复制
6
5 4 2 6 3 1
输出样例#1: 复制
11

说明

对于25%的数据,n≤2500n \leq 2500n2500

对于50%的数据,n≤4×104n \leq 4 \times 10^4n4×104。

对于所有数据,n≤5×105n \leq 5 \times 10^5n5×105

请使用较快的输入输出

应该不会n方过50万吧 by chen_zhe


 

这题其实就是考归并排序。

对于归并排序,这是一种体现分治思想的算法。

对于一段区间,我们先把它分成尽量相等的两部分,然后通过比较这两部分来进行排序。

具体来操作,我们先保存两个指针分别指向两部分最前面的元素,还有一个数组用来保存排序后的区间(从小到大排)。

如果左边部分指针指向的数小于等于右边部分指针指向的数,那么我们就把那个数放进新定义的数组里,然后左边的指针往后移一位(反之同理)。

当某一部分的数全部放到了新定义的数组中后,我们再把另一部分剩下的放进去,于是我们的归并排序就这样完成了。

 

对于这一题中要求的逆序对数量,我们可以在归并排序中实现求解。

我们发现如果右边部分指针指向的数比左边的小时我们确定这个数肯定小于左边部分剩余的数,所以我们就加上左边剩余数的数量即可。

#include<iostream>
#include<cstdlib>
#include<cstring>
#include<cstdio>
#include<algorithm>

#define ll long long
#define il inline
#define db double

using namespace std;

il int gi()
{
  int x=0,y=1;
  char ch=getchar();
  while(ch<'0'||ch>'9')
    {
      if(ch=='-')
	y=-1;
      ch=getchar();
    }
  while(ch>='0'&&ch<='9')
    {
      x=x*10+ch-'0';
      ch=getchar();
    }
  return x*y;
}

int a[1000045],t[1000045];
ll ans;

il void merge(int l,int r)
{
  if(l==r)
    return;
  int m=(l+r)>>1;
  merge(l,m);
  merge(m+1,r);
  int i=l,j=m+1,k=l;
  while(i<=m&&j<=r)
    {
      if(a[i]<=a[j])
	t[k++]=a[i++];
      else
	{
	  t[k++]=a[j++];
	  ans+=m-i+1;
	}
    }
  while(i<=m)
    t[k++]=a[i++];
  while(j<=r)
    t[k++]=a[j++];
  for(int i=l;i<=r;i++)
    a[i]=t[i];	
}

int main()
{
  int n=gi();
  for(int i=1;i<=n;i++)
    a[i]=gi();
  merge(1,n);
  printf("%lld\n",ans);
  return 0;
}

 

posted @ 2018-10-28 21:45  GSHDYJZ  阅读(172)  评论(0编辑  收藏  举报