学习笔记:逆序对

逆序对

引入

对于给定的一段正整数序列,逆序对就是序列中 $a_i>a_j$ 且 $i<j$ 的有序对。

—— 洛谷 P1908 逆序对

实现

首先贴出一道例题。

洛谷 P1908 逆序对

关于逆序对的定义:对于给定的一段正整数序列,逆序对就是序列中 $a_i>a_j$ 且 $i<j$ 的有序对。对于给定的一段正整数,试求出序列中逆序对的数目。注意序列中可能有重复数字。其中对于所有数据,$n \leq 5 \times 10^5$。

一眼逆序对

我们可以考虑直接枚举,显然答案有:$$ \sum_{i=1}^n\sum_{j=1}^{i-1}[a_i<a_j] $$ 总的时间复杂度 $O(n^2)$,也许可以过。于是很快就写出了一份码:

#include <iostream>
#define int long long
#define MAXN 500005
using namespace std;
int n, a[MAXN], ans;
int read(){
    int t = 1, x = 0;char ch = getchar();
    while(!isdigit(ch)){if(ch == '-')t = -1;ch = getchar();}
    while(isdigit(ch)){x = (x << 1) + (x << 3) + (ch ^ 48);ch = getchar();}
    return x * t;
}
signed main(){
    n = read();
    for(int i = 1 ; i <= n ; i ++)a[i] = read();
    for(int i = 1 ; i <= n ; i ++)
        for(int j = 1 ; j < i ; j ++)
            if(a[i] < a[j])ans++;
    cout << ans << endl;return 0;
}

然而:

看来暴力显然是不够的……

需要一些乱搞。

需要一些高级优化。

考虑树状数组。首先对于逆序对,我们可以如此思考这个问题:对于每个数的逆序对,在一个数列中的数量都是一定的。那么我们可以对于数列 $a$ 的每一个 $a_i$,可以与 $a_1-a_{i-1}$ 匹配出 $k_i$ 个逆序对,那么易知答案为:$$ \sum_{i=1}^nk_i $$ 首先离散化一下,再按价值从大到小排序。排完序之后用树状数组维护,每次把这个数的位置加入到树状数组中(因为是排完序之后,所以之前加入的一定比后加入的大),然后在查询当前这个数前面位置的数(是前面位置的数,要当前这个数 $-1$),就是逆序对的个数了。

#include <iostream>
#include <algorithm>
#define int long long
#define MAXN 500005
using namespace std;
int n, tree[MAXN], ans;
struct node{
    int a, b;
    bool friend operator<(node a, node b){
        if(a.a == b.a)return a.b > b.b;
        else return a.a > b.a;
    }
}a[MAXN];
int read(){
    int t = 1, x = 0;char ch = getchar();
    while(!isdigit(ch)){if(ch == '-')t = -1;ch = getchar();}
    while(isdigit(ch)){x = (x << 1) + (x << 3) + (ch ^ 48);ch = getchar();}
    return x * t;
}
void write(int x){
    if(x < 0){putchar('-');x = -x;}
    if(x >= 10)write(x / 10);
    putchar(x % 10 ^ 48);
}
int lowbit(int x){return x & -x;}
void update(int x, int k){
    for(int i = x ; i <= n ; i += lowbit(i))
        tree[i] += k;
}
int query(int x){
    int res = 0;
    for(int i = x ; i >= 1 ; i -= lowbit(i))
        res += tree[i];
    return res;
}
signed main(){
    n = read();
    for(int i = 1 ; i <= n ; i ++)a[i].a = read();
    for(int i = 1 ; i <= n ; i ++)a[i].b = i;
    sort(a + 1, a + n + 1);
    for(int i = 1 ; i <= n ; i ++)
        update(a[i].b, 1),ans += query(a[i].b - 1);
    write(ans);putchar('\n');return 0;
}

然后就切了……

posted @ 2023-10-11 18:58  tsqtsqtsq  阅读(42)  评论(0)    收藏  举报  来源