题目描述
猫猫 TOM 和小老鼠 JERRY 最近又较量上了,但是毕竟都是成年人,他们已经不喜欢再玩那种你追我赶的游戏,现在他们喜欢玩统计。
最近,TOM 老猫查阅到一个人类称之为“逆序对”的东西,这东西是这样定义的:对于给定的一段正整数序列,逆序对就是序列中 a i > a j a_i>a_j ai>aj 且 i < j i<j i<j 的有序对。知道这概念后,他们就比赛谁先算出给定的一段正整数序列中逆序对的数目。注意序列中可能有重复数字。
输入格式
第一行,一个数 n n n,表示序列中有 n n n个数。
第二行 n n n 个数,表示给定的序列。序列中每个数字不超过 1 0 9 10^9 109。
输出格式
输出序列中逆序对的数目。
样例 #1
样例输入 #1
6
5 4 2 6 3 1
样例输出 #1
11
思路
这里用到了一个离散化的思想:假设原本的数组是这个样子的{1, 2, 5, 900},我们要对每个元素出现的次数进行计数,那我们就需要开长度为900的数组,但是我们求解逆序对的时候并不需要知道每个数字的具体大小,只要知道他们之间的相对大小关系即可。
所以我们用一个数组d来存放这种相对关系:d[i]代表原数组中第i小的元素的下标。这样就把元素的相对大小的信息存储下来了。
接下来我们就要利用这个d数组来求解原数组中的逆序对的个数了,仔细思考一下,发现原数组中逆序对的个数就是d数组中正序对的个数。
如何利用树状数组来求解答案呢?此时树状数组维护的信息是第[1, x]小的数字的个数。那么我们把d数组中的元素依次加入树状数组中,每次插入时记录下来getsum(d[i]-1)的值,这些值的累加就是答案。
代码
#include <cstdio>
#include <algorithm>
using namespace std;
typedef long long ll;
const int maxn = 5e5+5;
int a[maxn], d[maxn], c[maxn];
int n;
int lowbit(int x) {
return x&(-x);
}
void add(int i) {
while(i <= n) {
++c[i];
i += lowbit(i);
}
}
ll getsum(int i) {
ll res = 0;
while(i > 0) {
res += c[i];
i -= lowbit(i);
}
return res;
}
bool cmp(int i, int j) {
if(a[i] == a[j]) return i > j; // 当原数组中出现相同的元素时,我们把d数组中较大的下标排在前面,防止相同的元素也被记到逆序对中
return a[i]>a[j];
}
int main(void)
{
scanf("%d", &n);
for(int i = 1; i <= n; ++i) {
scanf("%d", &a[i]);
d[i] = i;
}
stable_sort(d+1, d+n+1, cmp);
ll ans = 0;
for(int i = 1; i <= n; ++i) {
add(d[i]);
ans += getsum(d[i]-1);
}
printf("%lld\n", ans);
return 0;
}