##[$AcWing$ $788$. 逆序对的数量](https://www.acwing.com/problem/content/790/) ### 一、题目描述 给定一个长度为 $n$ 的整数数列,请你计算数列中的逆序对的数量。 逆序对的定义如下:对于数列的第 $i$ 个和第 $j$ 个元素,如果满足 $ia[j]$,则其为一个逆序对;否则不是。 **输入格式** 第一行包含整数 $n$,表示数列的长度。 第二行包含 $n$ 个整数,表示整个数列。 **输出格式** 输出一个整数,表示逆序对的个数。 **数据范围** $1≤n≤100000$,数列中的元素的取值范围 $[1,10^9]$ **输入样例:** ```cpp {.line-numbers} 6 2 3 4 5 6 1 ``` **输出样例:** ```cpp {.line-numbers} 5 ``` ### 二、解题思路 #### 梳理概念 逆序对,就是对于任意两个序列中的数字,值大的,反而出现在前方,就是一个逆序对。 #### 求解思路 ① 由于是顺序出现了问题,才会产生逆序对,所以,结果肯定与顺序相关,也就是,需要进行排序。 ② 按什么进行排序呢?无外乎是按值,或者是按输入顺序。 那我们按顺序来吧,然后将树状数组的个数定义成数值的上限?一看$1e9$,直接傻了吧,开不了那么大的数组。 没办法,只能是按值来了。 将数组元素包装成结构体,然后按数值由大到小进行排序。(其实由小到大也是一样的,可以自行推导一下) 那要是两个数的数值一样呢?那谁在前谁在后呢? > **注意**:这道题自定义排序参数$cmp$的实现,不能单纯的$val using namespace std; typedef long long LL; const int N = 500010; int n; int a[N], q[N], ql; // 二分模板 lower_bound (左闭右开) int find(int x) { return lower_bound(q + 1, q + 1 + ql, x) - q; } // 树状数组模板 #define lowbit(x) (x & -x) int c[N]; void add(int x, int v) { while (x < N) c[x] += v, x += lowbit(x); } LL sum(int x) { LL res = 0; while (x) res += c[x], x -= lowbit(x); return res; } int main() { scanf("%d", &n); for (int i = 1; i <= n; i++) scanf("%d", &a[i]), q[i] = a[i]; // 复制出来一份q[i],用于排序+去重=离散化数组 // 离散化 = 由小到大排序+去重 sort(q + 1, q + n + 1); // 排序后,原数组原地去重,得到离散化后的数组 ql = 1; for (int i = 2; i <= n; i++) if (q[i] != q[ql]) q[++ql] = q[i]; LL res = 0; for (int i = 1; i <= n; i++) { // 捋着原数组来 int x = find(a[i]); // 通过二分算法,找到a[i]映射的 离散化后q[]数组中找到对应的新下标 // 由于数值排序是由小到大进行的,所以在我之前进入树状数组的,肯定是数值比我小的,同时,它们的位置还在我后面,就是逆序 res += sum(ql) - sum(x); // sum(bl)-sum(x):从[x+1~bl]这段区间内的元素个数数量,也就是在枚举到x这个数时,已经产生的比x这个数大的有多少个 add(x, 1); // 下标为x的位置,个数数量+1 } // 输出结果 printf("%lld", res); return 0; } ```