[简记] 在nlogn时间计算左/右逆序数计数

π 为一个排列,如果 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)

[简记] 在nlogn时间计算左/右逆序数计数_第1张图片

使用归并排序计算左逆序计数

#include 
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 
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 
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 
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)

你可能感兴趣的:(排序)