逆序对

直达原题

题目描述

猫猫 TOM 和小老鼠 JERRY 最近又较量上了,但是毕竟都是成年人,他们已经不喜欢再玩那种你追我赶的游戏,现在他们喜欢玩统计。

最近,TOM 老猫查阅到一个人类称之为“逆序对”的东西,这东西是这样定义的:对于给定的一段正整数序列,逆序对就是序列中 \(a_i>a_j\)\(i<j\) 的有序对。知道这概念后,他们就比赛谁先算出给定的一段正整数序列中逆序对的数目。注意序列中可能有重复数字。

Update:数据已加强。

输入格式

第一行,一个数 \(n\),表示序列中有 \(n\)个数。

第二行 \(n\) 个数,表示给定的序列。序列中每个数字不超过 \(10^9\)

输出格式

输出序列中逆序对的数目。

样例 #1

样例输入 #1

6
5 4 2 6 3 1

样例输出 #1

11

提示

对于 \(25\%\) 的数据,\(n \leq 2500\)

对于 \(50\%\) 的数据,\(n \leq 4 \times 10^4\)

对于所有数据,\(n \leq 5 \times 10^5\)

请使用较快的输入输出

应该不会 \(O(n^2)\) 过 50 万吧 by chen_zhe

分析1

对于这道题如果直接用暴力法时间复杂度O(n2)超时,可以使用归并排序法,先二分为两部分,因为按照原有顺序,左区间的序号是小于右区间的,所以直接比较左右区间里面的元素值,因为左右区间里的元素都是排过序的,如果左区间里i的元素值大于右区间j的元素值,那么i区间之后的元素都比j的元素大,此时ans=mid-i+1,然后对j++进行下一次比较累加ans,如果i的元素值小于j那么就i++。

代码实现

#include <bits/stdc++.h>
using namespace std;

const int MAXN = 500005;
int tmp[MAXN], q[MAXN];
long long ans = 0;

void merge_sort(int l, int r) {
    if (l >= r) return;
    int mid = l + r >> 1;
    merge_sort(l, mid);
    merge_sort(mid + 1, r);
    int k = 0, i = l, j = mid + 1;
    while (i <= mid && j <= r)
        if (q[i] <= q[j])
            tmp[k++] = q[i++];
        else {
            tmp[k++] = q[j++];
            ans += mid - i + 1;
        }
    while (i <= mid) tmp[k++] = q[i++];
    while (j <= r) tmp[k++] = q[j++];
    for (i = l, j = 0; i <= r; ++i, ++j)
        q[i] = tmp[j];
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);

    int n;
    cin >> n;
    for (int i = 1; i <= n; ++i) {
        cin >> q[i];
    }
    merge_sort(1, n);
    cout << ans << endl;

    return 0;
}
posted @ 2023-07-29 00:24  LongDz  阅读(15)  评论(0)    收藏  举报