设 π 为一个排列,如果 i<j 而且 π(i)>π(j) ,这个序对 (i,j) 或这一对元素 (π(i),π(j)) 被称为是 π 的一个逆序。
逆序数是逆序集的基数,它常用于量度排列或序列的已排序程度。
如果要求出一个排序的逆序数,我们可以通过归并排序或树状数组的方式实现。但有些题目要求我们求出排序的左逆序计数或右逆序计数时,就要改造一下常见方法
逆序向量 v :
v(i) 是在 π 之中的 i 之前,元素较i 大的数量
v(i)=#{k|k>i∧ π−1(k)<π−1(i)}
左逆序计数 l :l(i) 在 π(i) 中的在 π(i) 之前,元素较 π(i) 大的数量
l(i)=#{k|k>i∧ π(k)>π(i)}
右逆序计数 r :r(i) 在 π(i) 中的在 π(i) 之后,元素较 π(i) 小的数量
r(i)=#{k|k>i∧ π(k)<π(i)}
可知存在
∑v(i)=∑l(i)=∑r(i)
使用归并排序计算左逆序计数
#include <bits/stdc++.h>
using namespace std;
const int N = 1e5+5;
struct Node{
int val, pos;
}a[N], tmp[N];
int ans[N];
int n;
void mergeSort(int l, int r){
if(l+1 < r){
int mid = l+(r-l)/2;
mergeSort(l, mid);
mergeSort(mid, r);
int lp = l, rp = mid, t = l;
while(lp < mid && rp < r){
if(a[lp].val <= a[rp].val) tmp[t++] = a[lp++];
else{ans[a[rp].pos]+=mid-lp; tmp[t++] = a[rp++];}
}
while(lp < mid) tmp[t++] = a[lp++];
while(rp < r) tmp[t++] = a[rp++];
for(int i = l; i < r; i++)
a[i] = tmp[i];
}
}
int main()
{
scanf("%d", &n);
for(int i = 0; i < n; i++){
scanf("%d", &a[i].val);
a[i].pos = i;
}
mergeSort(0, n);
for(int i = 0; i < n; i++){
printf("%d%c", ans[i], i==n-1?'\n':' ');
}
return 0;
}
使用树状数组计算左逆序计数
#include <bits/stdc++.h>
using namespace std;
const int N = 1e5+5;
int n;
int a[N], bit[N];
void add(int x){
for(;x<=N; x+=x&(-x)) bit[x]++;
}
int sum(int x){
int s = 0;
for(; x>0; x-=x&(-x)) s+=bit[x];
return s;
}
int main()
{
scanf("%d", &n);
for(int i = 0; i < n; i++) scanf("%d", &a[i]);
for(int i = 0; i < n; i++){
add(a[i]+1);
printf("%d%c", i-sum(a[i]), i==n-1?'\n':' ');
}
return 0;
}
使用归并排序计算右逆序计数
#include <bits/stdc++.h>
using namespace std;
const int N = 1e5+5;
struct Node{
int val, pos;
}a[N], tmp[N];
int ans[N];
int n;
void mergeSort(int l, int r){
if(l+1 < r){
int mid = l+(r-l)/2;
mergeSort(l, mid);
mergeSort(mid, r);
int lp = l, rp = mid, t = l;
while(lp < mid && rp < r){
if(a[lp].val <= a[rp].val) {ans[a[lp].pos]+=rp-mid; tmp[t++] = a[lp++];}
else tmp[t++] = a[rp++];
}
while(lp < mid) {ans[a[lp].pos]+=rp-mid; tmp[t++] = a[lp++];}
while(rp < r) tmp[t++] = a[rp++];
for(int i = l; i < r; i++)
a[i] = tmp[i];
}
}
int main()
{
scanf("%d", &n);
for(int i = 0; i < n; i++){
scanf("%d", &a[i].val);
a[i].pos = i;
}
mergeSort(0, n);
for(int i = 0; i < n; i++){
printf("%d%c", ans[i], i==n-1?'\n':' ');
}
return 0;
}
使用树状数组计算右逆序计数
#include <bits/stdc++.h>
using namespace std;
const int N = 1e5+5;
int n;
int a[N], bit[N], ans[N];
void add(int x){
for(;x<=N; x+=x&(-x)) bit[x]++;
}
int sum(int x){
int s = 0;
for(; x>0; x-=x&(-x)) s+=bit[x];
return s;
}
int main()
{
scanf("%d", &n);
for(int i = 0; i < n; i++) scanf("%d", &a[i]);
for(int i = n-1; i >= 0; i--){
add(a[i]+1);
ans[i] = sum(a[i]);
}
for(int i = 0; i < n; i++)
printf("%d%c", sum(a[i]), i==n-1?'\n':' ');
return 0;
}
参考资料:https://en.wikipedia.org/wiki/Inversion_(discrete_mathematics)