链接:P1908
题目描述
猫猫TOM和小老鼠JERRY最近又较量上了,但是毕竟都是成年人,他们已经不喜欢再玩那种你追我赶的游戏,现在他们喜欢玩统计。最近,TOM老猫查阅到一个人类称之为“逆序对”的东西,这东西是这样定义的:对于给定的一段正整数序列,逆序对就是序列中ai>aj且i<j的有序对。知道这概念后,他们就比赛谁先算出给定的一段正整数序列中逆序对的数目。
Update:数据已加强。
输入格式
第一行,一个数n,表示序列中有n个数。
第二行n个数,表示给定的序列。序列中每个数字不超过10^910
9
输出格式
给定序列中逆序对的数目。
输入输出样例
输入 #1
6
5 4 2 6 3 1
输出 #1
11
说明/提示
对于25%的数据,n \leq 2500n≤2500
对于50%的数据,n \leq 4 \times 10^4n≤4×10
对于所有数据,n \leq 5 \times 10^5n≤5×10
请使用较快的输入输出
应该不会n方过50万吧 by chen_zhe
这道题不二分过不去数据范围太大了T_T
二分的思想就是分而治之
而我们可以明显的看出来,如果我们要用二分来查找一下逆序对
这个过程其实就是归并排序的过程
那么什么是归并排序
我们可以举几个数字来列举一下
比如有一个数列:
5 4 7 6 3 9
那么如果我们已经进入了归并排序的最后一步,此时已经分为了两个有序序列
4 5 7
3 6 9
那么我们便用4与第二个数组里面的内容全部比较一下,因为我们已
我们发现了4要比第二个数组的第一个数要大,那么肯定 3 一定会比第一个数组的其他数都要小(根据有序性)同时当前数组也是有序的,因此一定小于同数组的后面的数
那么我们就把3放入最终排序结果ans的数组的第一位上
ans[1]=3
此时
4 5 7
6 9
- 那么这个时候我们再用4与6比较,发现4 小了,根据有序性我们需要把4放入排序结果的数组中
- 因为4不见了,那么我们便用第一个数组中的第二个数5比较,我们发现了5也是小于6的根据有序性,则把5也放入排序结果的数组中
- 接下来我们再用第一个数组中的7比较,此时7大于6,那么根据有序性,我们就把6放入排序结果的数组中
- 接下来7再与9比较,此时9>7则把7放入存储结果的数组中 因为第一个数组没有数了
- 那么我们就跳出这个比较过程,再把第二个数组的所有数自动放在存储结果数组的队尾即可
若用归并排序的思想解决此题,我们同样也是如此
我们做一个从小到大归并排序,然后再刚才一个一个数字比较的过程中,如果第一个数组的数大于了第二个数组的某一个数,那么则说明出现了逆序对的现象,同时由于第一个数组本身就有序性,因为这个数已经大于了第二个数组的中的数a,那么在这个数后面的数也一定大于了第二个数组中的数a
那么如果我们发现了第一个数组的数大于了第二个数组的某一个数的情况
我们在更新存储结果的数组的同时,同时把记录存在逆序对的个数ans也更新一下
因为两个数组的分别上下界为:
第一个:L~ mid
第二个:mid+1~R
所以我们可以推出记录逆序对的个数
每次ans+=mid-i+1 (i为发现第一个数组的第i个数大于了第二个数组的某一个数)
最后输出这个ans即可
代码:
#include<cstdio>
#include<iostream>
using namespace std;
int n,a[1000001],c[1000001];
long long ans;
void CZP(int L,int R) //归并排序的过程
{
if(L>=R)
return;
int mid=(L+R)/2,i=L,j=mid+1,k=L;
CZP(L,mid),CZP(mid+1,R);
while(i<=mid&&j<=R)
{
if(a[i]<=a[j])
{
c[k]=a[i];
k++;i++;
}
else //出现了逆序对
{
c[k]=a[j];
k++;
j++;
ans+=mid-i+1; //累加逆序对个数
}
}
while(i<=mid){
c[k]=a[i];
k++;
i++;
}
while(j<=R){
c[k]=a[j];
k++;
j++;
}
for(int l=L;l<=R;l++) //更新原来数列,使得有序化
a[l]=c[l];
}
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
scanf("%d",&a[i]);
CZP(1,n);
printf("%lld",ans);
return 0;
}