EH's Blog

题目情境#

题目链接:https://www.luogu.com.cn/problem/P1908 ↗

题目描述

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

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

输入格式

第一行,一个数 nn,表示序列中有 nn 个数。

第二行 nn 个数,表示给定的序列。序列中每个数字不超过 10910^9。

输出格式

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

输入输出样例

输入 #1

6
5 4 2 6 3 1
plaintext

输出 #1

11
plaintext

说明/提示

对于所有数据,1≤n≤5×1051 \leq n \leq 5 \times 10^5。

算法解析#

归并排序

主要利用分治思想,时间复杂度 O(nlogn)O(nlogn)

  1. 对数列不断等长拆分,直到一个数的长度。
  2. 回溯时,按升序合并左右两段。
  3. 重复以上两个过程,直到递归结束。

Python代码实现#


import sys
sys.setrecursionlimit(1000000)

def merge(l, r):
    global res, a, b
    if l >= r:
        return
    mid = (l + r) // 2
    merge(l, mid)
    merge(mid + 1, r)

    i = l; j = mid + 1; k = l
    while i <= mid and j <= r:
        if a[i] <= a[j]:
            b[k] = a[i]
            k += 1; i += 1
        else:
            b[k] = a[j]
            k += 1; j += 1
            res += mid - i + 1
    while i <= mid:
        b[k] = a[i]
        k += 1; i += 1
    while j <= r:
        b[k] = a[j]
        k += 1; j += 1
    for i in range(l, r + 1):
        a[i] = b[i]

n = int(input())
a = list(map(int, input().split()))
b = [0] * n
res = 0
merge(0, n - 1)
print(res)
python

C++代码实现#

#include <iostream>
using namespace std;

int n,a[500010],b[500010];
long long res;

void merge(int l,int r){
  if(l>=r) return;
  int mid=l+r>>1;
  merge(l,mid);
  merge(mid+1,r); //拆分

  int i=l,j=mid+1,k=l; //合并
  while(i<=mid && j<=r){
    if(a[i]<=a[j]) b[k++]=a[i++];
    else b[k++]=a[j++], res+=mid-i+1;
  }
  while(i<=mid) b[k++]=a[i++];
  while(j<=r) b[k++]=a[j++];
  for(i=l; i<=r; i++) a[i]=b[i];
}

int main(){
  cin>>n;
  for(int i=0;i<n;i++) scanf("%d",&a[i]);
  merge(0,n-1);
  printf("%lld\n",res);
}
c
A14 归并排序、逆序对
https://ehnotgod.site/blog/a14-%E5%BD%92%E5%B9%B6%E6%8E%92%E5%BA%8F%E9%80%86%E5%BA%8F%E5%AF%B9
Author EH
Published at August 8, 2026