Bootstrap

洛谷 P1908:逆序对 ← 微调“归并排序”代码可得

【题目来源】
https://www.luogu.com.cn/problem/P1908

【题目描述】
猫猫 TOM 和小老鼠 JERRY 最近又较量上了,但是毕竟都是成年人,他们已经不喜欢再玩那种你追我赶的游戏,现在他们喜欢玩统计。
最近,TOM 老猫查阅到一个人类称之为“逆序对”的东西,这东西是这样定义的:对于给定的一段正整数序列,逆序对就是序列中 ai>aj 且 i<j 的有序对。知道这概念后,他们就比赛谁先算出给定的一段正整数序列中逆序对的数目。注意
序列中可能有重复数字

【输入格式】
第一行,一个数 n,表示序列中有 n 个数。
第二行 n 个数,表示给定的序列。序列中每个数字不超过
10^9

【输出格式】
输出序列中逆序对的数目。

【输入样例】
6
5 4 2 6 3 1

【输出样例】
11

【说明/提示】
对于 25% 的数据,n≤2500。
对于 50% 的数据,n≤4×10^4。
对于所有数据,n≤5×
10^5
请使用较快的输入输出。

【算法分析】
● 归并排序 → 逆序对
归并排序是一种有效的排序算法,它不仅可以对数组进行排序,还可以在排序的过程中
顺便计算逆序对的数量。逆序对是指数组中满足 i < j 但 a[i] > a[j] 的元素对 ( a[i], a[j] )。求逆序对数量的代码,只需微调归并排序代码即可。
本题的求逆序对代码,脱胎于如下归并排序代码。

#include <bits/stdc++.h>
using namespace std;
 
const int maxn=1e5+5;
int a[maxn],t[maxn];
int n;
 
void merge_sort(int le,int ri) {
    if(le==ri) return;
 
    int mid=(le+ri)>>1;
    merge_sort(le,mid);
    merge_sort(mid+1,ri);
 
    int p=le;
    int i=le,j=mid+1;
    while(i<=mid && j<=ri) { //double pointer
        if(a[i]<a[j]) t[p++]=a[i++];
        else t[p++]=a[j++];
    }
    while(i<=mid) t[p++]=a[i++];
    while(j<=ri) t[p++]=a[j++];
 
    for(int i=le; i<=ri; i++) a[i]=t[i];
}
 
int main() {
    scanf("%d",&n);
    for(int i=1; i<=n; i++) {
        scanf("%d",&a[i]);
    }
 
    merge_sort(1,n); //subscript
    for(int i=1; i<=n; i++) {
        if(i!=n) printf("%d ",a[i]);
        else printf("%d\n",a[i]);
    }
 
    return 0;
}
 
 
/*
in:
5
4 2 4 5 1

out:
1 2 4 4 5
*/

● 本题需注意序列中可能有重复数字。故代码第 19 行为 a[i]<=a[j],而不是 a[i]<a[j]。代码中第 22 行 cnt+=(mid-i+1),表示当 a[i]>a[j] 时,a[i],…,a[mid] 都与 a[j] 构成逆序对。

●​​​​​​​ 在 C++ 中,int 类型通常能表示的数值范围是从 -2,147,483,648 ~ 2,147,483,647‌,也即 -2^31 ~ 2^31-1,小于 2.15×10^9。简单起见,若整数大于 2×10^9,就选择使用 long long 型
详见:https://blog.csdn.net/hnjzsyjyj/article/details/132240042

● 在 C++ 中,若输入数据
个数大于 10^5 时,推荐使用 scanf 而不是 cin 输入数据。这是因为 scanf 通常比 cin 更快。
详见:https://blog.csdn.net/hnjzsyjyj/article/details/145618674

【算法代码】
下面是求逆序对数量的代码,脱胎归并排序代码。在其中,分别用 add code 1、add code 2、add code 3 标示了相对于归并排序代码添加的代码。

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

const int maxn=5e5+5;
int a[maxn],t[maxn];
long long cnt; //add code 1
int n;

void merge_sort(int le,int ri) {
    if(le==ri) return;

    int mid=(le+ri)>>1;
    merge_sort(le,mid);
    merge_sort(mid+1,ri);

    int p=le;
    int i=le,j=mid+1;
    while(i<=mid && j<=ri) { //double pointer
        if(a[i]<=a[j]) t[p++]=a[i++]; //a[i]<=a[j]
        else {
            t[p++]=a[j++];
            cnt+=(mid-i+1); //add code 2
        }
    }
    while(i<=mid) t[p++]=a[i++];
    while(j<=ri) t[p++]=a[j++];

    for(int i=le; i<=ri; i++) a[i]=t[i];
}

int main() {
    scanf("%d",&n);
    for(int i=1; i<=n; i++) {
        scanf("%d",&a[i]);
    }

    merge_sort(1,n); //subscript
    printf("%lld\n",cnt); //add code 3

    // If want to output the sorted array,
    // remove the following comments.
    /*for(int i=1; i<=n; i++) {
        if(i!=n) printf("%d ",a[i]);
        else printf("%d\n",a[i]);
    }*/

    return 0;
}


/*
in:
6
5 4 2 6 3 1

out:
11
*/





【参考文献】
https://blog.csdn.net/hnjzsyjyj/article/details/145679752
https://blog.csdn.net/hnjzsyjyj/article/details/132685017



 

;