https://www.luogu.org/problemnew/show/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≤2500
对于50%的数据,n≤4×10^4
对于所有数据,n≤5×10^5
请使用较快的输入输出
应该不会n方过50万吧 by chen_zhe
思路:逆序对数与冒泡排序的交换次数是相等的,但是很明显O(n^2)会炸,聪明的人类后来想到了分治法,只需稍微修改归并排序,就可以得到答案。归并排序将数组分成左右两部分,再将两部分合并起来。(当然 这个操作是递归的)那么我们只要知道了左半部分的逆序对数和右半部分的逆序对数,再加上合并过程中计算出的逆序对数不就得到答案了吗?关键就在于如何在合并过程中计算出逆序对数,假设当前合并过程中,左半部分的指针是i,右半部分的指针是j(下标),且左半部分一共有n1个元素,那么在L[i]>R[j]的时候,根据逆序数的定义,我们可以知道这时候贡献了n1-i个逆序数。(注意左半部分实际上是[0,n1)即左闭右开)那么问题就简单了。
#include<iostream>
#include<cstdio>
using namespace std;
const int MAX=500005;
int a[MAX];
int L[MAX/2];
int R[MAX/2];
long long Merge(int l,int mid,int r);
long long MergeSort(int l,int r);
int main()
{
int n;
scanf("%d",&n);
for(int i=0;i<n;i++)
scanf("%d",&a[i]);
cout<<MergeSort(0,n)<<endl;
return 0;
}
long long Merge(int l,int mid,int r)
{
long long num=0;
int temp1=mid-l;
int temp2=r-mid;
for(int i=0;i<temp1;i++)
L[i]=a[l+i];
for(int i=0;i<temp2;i++)
R[i]=a[mid+i];
int i=0,j=0,k=l;
while(i<temp1&&j<temp2)
{
if(L[i]<=R[j])
a[k++]=L[i++];
else
{
num+=temp1-i;
a[k++]=R[j++];
}
}
while(i!=temp1)
a[k++]=L[i++];
while(j!=temp2)
a[k++]=R[j++];
return num;
}
long long MergeSort(int l,int r)
{
if(l+1<r)//最少两个元素
{
int mid=(l+r)/2;
long long n1,n2,n3;
n1=MergeSort(l,mid);
n2=MergeSort(mid,r);
n3=Merge(l,mid,r);
return n1+n2+n3;
}
return 0;
}