题目描述
猫猫 TOM 和小老鼠 JERRY 最近又较量上了,但是毕竟都是成年人,他们已经不喜欢再玩那种你追我赶的游戏,现在他们喜欢玩统计。
最近,TOM 老猫查阅到一个人类称之为“逆序对”的东西,这东西是这样定义的:对于给定的一段正整数序列,逆序对就是序列中
a
i
>
a
j
a_i>a_j
ai>aj
且
i
<
j
i<j
i<j 的有序对。知道这概念后,他们就比赛谁先算出给定的一段正整数序列中逆序对的数目。注意序列中可能有重复数字。
Update:数据已加强。
输入格式
第一行,一个数 n,表示序列中有 n个数。
第二行 n 个数,表示给定的序列。序列中每个数字不超过 1 0 9 10^9 109
输出格式
输出序列中逆序对的数目。
输入输出样例
输入
6
5 4 2 6 3 1
输出
11
分析
求逆序对最典型的方法就是归并排序,但是还有一种方法就是树状数组。(个人感觉树状数组求逆序对相比归并排序排序要更好理解一些,而且树状数组的代码量也要少一些)
一、 离散化
数据很大,这会造成我们要开的空间很大,要用离散化压缩数组(可以保证小的数依旧小,大的数依旧大,对结果没有影响)
👉什么是离散化?
二、求逆序对
对于离散化后的数组dis,我们可以用桶排序的思想,将
a
[
d
i
s
[
i
]
]
a[dis[i]]
a[dis[i]]加上1,然后我们统计a[1] 到a[dis[i]-1]的和ans,就是在这个数前面有多少个数比它小。
AC的C++代码
#include <iostream>
#include <algorithm>
#define ll long long int
using namespace std;
const ll MAXN=1e5+5;
//a维持树状数组,b为输入的原始数组,dis为b离散化之后的
ll a[MAXN*5],b[MAXN*5],dis[MAXN*5],n;
ll lowbit(ll x) {
return x & -x;
}
// 单点修改(第x个整数加v)
void update(ll x, ll v) {
for(ll i = x; i<= n; i += lowbit(i))
a[i] += v;
}
// 单点查询
ll getsum(ll x) { // a[1]……a[x]的和
ll sum = 0;
for(ll i = x; i> 0; i -= lowbit(i)){ // 注意是>0
sum += a[i];
}
return sum;
}
int main(){
cin >> n;
for(ll i=1; i<=n; i++)
cin>>b[i];
// 离散化
for(ll i=1; i <= n; i++){
dis[i]=b[i];
}
std::sort(b + 1, b + 1 + n);
ll len = std::unique(b + 1, b + n + 1) - b - 1;// len 为离散化后数组的有效长度(去重之后)
for(ll i=1; i <= n; i++)
dis[i] = std::lower_bound(b + 1, b + len + 1, dis[i]) - b;
ll ans=0;
for(ll i=n; i >= 1; i--){
update(dis[i], 1);
ans += getsum(dis[i]-1); //得到之前有多少个比你大的数(逆序对)
}
cout<<ans;
return 0;
}