题目描述
P1908 逆序对
猫猫 TOM 和小老鼠 JERRY 最近又较量上了,但是毕竟都是成年人,他们已经不喜欢再玩那种你追我赶的游戏,现在他们喜欢玩统计。
最近,TOM 老猫查阅到一个人类称之为“逆序对”的东西,这东西是这样定义的:对于给定的一段正整数序列,逆序对就是序列中 ai>aj
且 i<j 的有序对。知道这概念后,他们就比赛谁先算出给定的一段正整数序列中逆序对的数目。注意序列中可能有重复数字。
思路
-
暴力求解
如果暴力求解,那么思路很简单
从后往前找,先从末尾开始
拿当前这个数与之后的数比较
如果比当前数小就直接ans++,
最后枚举完了,就是最终答案
时间复杂度 O(n*n) TLE -
分治
如果求一个区间内的逆序对,是不是可以转化为求两个区间内的逆序对
(那么就会有同学问,你本来一个区间内的逆序对,可能会拆开啊,计算时不会被计算到啊?)
这个问题很简单嘛,你直接再拿两个区间的数再比较就彳亍
实现:归并排序
这时又有一个问题,归并排序,是会排序的啊,会打乱原来顺序啊
其实并不影响,因为比较的是两个区间内的各个数的大小,而左区间的下标一定比右区间的下标小,即使区间内排了序也不影响
遗留问题:既然利用归并排序,如果每次像暴力一样比较,那也会使时间复杂度变为O(n*n),所以如果计算是关键
其实归并时,就在比较两个数的大小,如果左区间的数一直小于等于右区间的数,直到碰到右区间的一个数小于左区间的数,是不是之后的所有数,即[i,mid]的数(i为左区间位置 都与当前这个数互为逆序对,所以此时答案就可以直接加上前面剩下左区间的个数 -
树状数组
可能有同学就会问了,树状数组不是用来计算区间和吗,这个逆序对和区间合有毛的关系?
这里就很巧妙了
回到暴力的想法,有两步
第一步,从后往前枚举
第二步,拿当前这个数与前面的所有数一一比较
第一步,不能再优化了(你每个都不枚举一下,答案就会有错)
第二步,其实是在寻找比 枚举到当前数 小的数,这里就可以树状数组来查询,每次被枚举后的数需要更行sum[1,a[i] ]
时间复杂度O(n*logn)
注意:如果a[i]的范围过大 需要离散化
当然其实也可以从前往后枚举,读者可以自行思考
代码
归并排序
#include<cstdio>
#include<cstring>
#include<iostream>
using namespace std;
int n;
const int maxn=5*1e5+100;
int m[maxn];
int mn[maxn];
long long ans=0;
void msort(int a,int b){
//因为左区间的下标始终大于右区间下标,所以就算排序了也不影响比较大小 </