逆序对的定义
在一个序列中,若存在i < j,a[i] > a[j],那么a[i] 和 a[j]就是一对逆序对
归并排序求逆序对
归并排序简单来说就是每次把序列二分,然后合并两个有序序列
在合并两个有序序列的过程中,可以用两个指针i,j分别对两个序列进行扫描,每次将a[i] a[j]中较小的那一个放到临时数组中
若a[j] < a[i],因为两个序列都是有序序列,那么a[i]后面的所有数字都要比a[j]大
也就是说a[i]后面的每个数字都可以和a[j]构成逆序对,所以将a[i]后面的数字数量加入答案
代码
#include<bits/stdc++.h>
using namespace std;
int n;
int a[30010];
int b[30010];
int cnt;
inline int read() {
int x = 0, f = 1; char ch = getchar();
while (ch < '0' || ch > '9') {if (ch == '-') f = -1; ch = getchar();}
while (ch >= '0' && ch <= '9') {x = (x << 1) + (x << 3) + (ch ^ 48); ch = getchar();}
return x * f;
}
void merge(int l, int r) {
if (l == r) return;
int mid = l + r >> 1;
int i = l, j = mid + 1;
merge(l, mid);
merge(mid + 1, r);
for (int k = l; k <= r; k++) {
if (j > r || i <= mid && a[i] < a[j]) b[k] = a[i++];
else cnt += mid - i + 1, b[k] = a[j++];
}
for (int k = l; k <= r; k++) a[k] = b[k];
}
int main() {
n = read();
for (int i = 1; i <= n; i++) a[i] = read();
merge(1, n);
printf("%d", cnt);
}
树状数组求逆序对
首先,我们可以进行一下离散化。
离散化简单来说就是用一个结构体存下序列的值和对应的编号,然后按序列值升序排序,取出编号代替序列值进行计算。
当然,使用离散化求逆序对有些浪费了。
我们可以用cnt[val]表示val在集合a中出现的次数,然后用一个树状数组维护前缀和。
主要流程:
倒序扫描给定序列;
对于序列中的每个数查询前缀和累加,cnt[a[i]]++然后维护前缀和。
输出ans;
代码(离散化)
#include<bits/stdc++.h>
using namespace std;
struct node {
int id, a;
} e[30010];
int n;
int ans;
int tree[30010];
inline int read() {
int x = 0, f = 1; char ch = getchar();
while (ch < '0' || ch > '9') {if (ch == '-') f = -1; ch = getchar();}
while (ch >= '0' && ch <= '9') {x = (x << 1) + (x << 3) + (ch ^ 48); ch = getchar();}
return x * f;
}
int ask(int x) {
int sum = 0;
for (; x; x -= (x & -x)) sum += tree[x];
return sum;
}
bool cmp(node a, node b) {
return a.a < b.a;
}
void add(int x, int y) {
for (; x <= 30000; x += (x & -x)) tree[x] += y;
}
int main() {
n = read();
for (int i = 1; i <= n; i++) {
e[i].a = read();
e[i].id = i;
}
sort(e + 1, e + n + 1, cmp);
for (int i = 1; i <= n; i++) {
add(e[i].id, 1);
ans += (i - ask(e[i].id));
}
printf("%d", ans);
}
代码(无离散化)
#include<bits/stdc++.h>
using namespace std;
int n;
int a[31010];
int tree[31010];
int ans;
inline int read() {
int x = 0, f = 1; char ch = getchar();
while (ch < '0' || ch > '9') {if (ch == '-') f = -1; ch = getchar();}
while (ch >= '0' && ch <= '9') {x = (x << 1) + (x << 3) + (ch ^ 48); ch = getchar();}
return x * f;
}
int ask(int x) {
int sum = 0;
for (; x; x -= (x & -x)) sum += tree[x];
return sum;
}
void add(int x, int y) {
for (; x <= 30009; x += (x & -x)) tree[x] += y;
}
int main() {
n = read();
for (int i = 1; i <= n; i++) a[i] = read();
for (int i = n; i >= 1; i--) {
ans += ask(a[i] - 1);
add(a[i], 1);
}
printf("%d", ans);
}