逆序数
问题:在一个排列中,如果前面的数大于后面的数,则称为一个逆序,任意两数构成的逆序之和称为这个排列的逆序数。现给出数字序列,求逆序数。
方法1:暴力求解,时间复杂度为O(n^2)。
方法2:是在归并排序的过程中进行统计,时间复杂度为O(nlogn),是最快的方法。
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
int n, a[50005], t[50005], ans;
void merge_sort(int l, int r) {
if (l + 1 >= r) return;
int m = (l + r) / 2;
merge_sort(l, m);
merge_sort(m, r);
int i = l, j = m, k = l;
while (i < m && j < r) {
if (a[i] <= a[j]) {
t[k++] = a[i++];
} else if (a[i] > a[j]) {
t[k++] = a[j++];
ans += m - i;
}
}
while (i < m) t[k++] = a[i++];
while (j < r) t[k++] = a[j++];
for (i = l; i < r; i++) a[i] = t[i];
}
int main() {
scanf("%d", &n);
for (int i = 0; i < n; i++)
scanf("%d", &a[i]);
merge_sort(0, n);
printf("%d\n", ans);
return 0;
}
方法3:利用bit可快速插点问线的性质,将插点操作时增加的值设为1,即可实现计数的功能。为方便统计,在插入时要按一定的顺序进行。每个元素有两个属性:下标和值,因而有两种做法。
第1种做法是按下标顺序挨个加入,bit的下标表示值,在加入某个元素时,bit中元素的下标都比它要小,所以要统计值比它大的元素个数,由于本题中元素值的取值范围太大,数组开不下,需要先离散化。
#include <bits/stdc++.h>
using namespace std;
int n, m, a[50005], b[50005], c[50005], f[50005];
int lowbit(int x) {
return x & -x;
}
void add(int x, int v) {
while (x <= n) {
f[x] += v;
x += lowbit(x);
}
}
int sum(int x) {
int ret = 0;
while (x > 0) {
ret += f[x];
x -= lowbit(x);
}
return ret;
}
int main() {
scanf("%d", &n);
for (int i = 0; i < n; i++) {
scanf("%d", &a[i]);
b[i] = a[i];
}
sort(b, b+n);
m = unique(b, b+n) - b;
for (int i = 0; i < n; i++)
c[i] = lower_bound(b, b + m, a[i]) - b;
int ans = 0;
for (int i = 0; i < n; i++) {
ans += sum(n) - sum(c[i]+1);
add(c[i]+1, 1);
}
printf("%d\n", ans);
return 0;
}
第2种做法是按元素值从小到大的顺序依次插入,bit的下标代表元素下标,在加入某个元素时,bit中元素的值均不超过它,需要统计bit中下标比它大的元素个数。
#include <bits/stdc++.h>
using namespace std;
struct st {
int x, v;
}a[50005];
int n, f[50005];
int lowbit(int x) {
return x & -x;
}
void add(int x, int v) {
while (x <= n) {
f[x] += v;
x += lowbit(x);
}
}
int sum(int x) {
int ret = 0;
while (x > 0) {
ret += f[x];
x -= lowbit(x);
}
return ret;
}
bool cmp(st p, st q) {
if (p.v != q.v) return p.v < q.v;
return p.x < q.x;
}
int main() {
scanf("%d", &n);
for (int i = 0; i < n; i++) {
scanf("%d", &a[i].v);
a[i].x = i;
}
sort(a, a+n, cmp);
int ans = 0;
for (int i = 0; i < n; i++) {
ans += sum(n) - sum(a[i].x+1);
add(a[i].x+1, 1);
}
printf("%d\n", ans);
return 0;
}
这两种做法的时间复杂度都是O(nlogn),但常数比归并排序要大一些。