信息学奥赛一本通 1311:【例2.5】求逆序对 | 1237:求排列的逆序数 | OpenJudge NOI 2.4 7622:求排列的逆序数 | 洛谷 P1908 逆序对

本文讲解了如何利用归并排序的特性高效求解逆序对问题,通过归并过程中逆序对数量的变化规律,将时间复杂度降低到O(nlogn),特别适用于大规模数据,如10^5规模。同时涉及到了多个编程挑战的题目链接和输入限制说明。
摘要由CSDN通过智能技术生成

【题目链接】

ybt 1311:【例2.5】求逆序对
ybt 1237:求排列的逆序数
OpenJudge NOI 2.4 7622:求排列的逆序数
洛谷 P1908 逆序对
ybt 1311,1237 OpenJudge 2.4 7622几题输入数据的最大个数为 1 0 5 10^5 105
洛谷 P1908 输入数据的最大个数为 5 ∗ 1 0 5 5*10^5 5105

【题目考点】

1. 归并排序 求逆序对

【君义精讲】排序算法

【解题思路】

如果直接写两层循环寻找逆序对,时间复杂度是 O ( n 2 ) O(n^2) O(n2),该题问题规模为 1 0 5 10^5 105,会超时。
可以用归并排序解决逆序对问题

例:某一次归并排序,两个数组分别为a1,a2,临时数组为t,逆序对数量s:
a1:1 6 8 a2:2 3 7 t: s:0
第一步:a1的1进入到临时数组t中,1和a2中的数字不产生逆序对
a1:6 8 a2:2 3 7 t:1 s:0
第二步:a2的2比a1的6小,2进入t中。2和a1中的所有元素都形成逆序对,逆序对数量增加2
a1:6 8 a2:3 7 t:1 2 s:2
第三步:a2的3比a1的6小,3进入t中。3和a1中的所有元素都形成逆序对,逆序对数量增加2
a1:6 8 a2:7 t:1 2 3 s:4
第四步:a1的6比a2的7小,6进入t中。不产生逆序对
a1:8 a2:7 t:1 2 3 6 s:4
第五步:a2的7比a1的8小,7进入t中。产生逆序对1个
a1:8 a2: t:1 2 3 6 7 s:5
最后把8 填到t中
a1: a2: t:1 2 3 6 7 8 s:5
此次归并一共找到5个逆序对

总结规律:在合并有序序列时,当a2数组的最小值比a1数组最小值小的时候,逆序对增加的数量为:此时a1数组剩余元素的个数。其他时刻,逆序对不增加。
根据这一原理编写程序,归并排序的时间复杂度是 O ( n l o g n ) O(nlogn) O(nlogn),可以解决规模为 1 0 5 10^5 105的问题

n个元素,当整个数组是严格降序序列时,有最多的逆序对:
第1元素和后面n-1个元素构成n-1个逆序对;
第2元素和后面n-2个元素构成n-2个逆序对;
。。。
第n-1元素和第n元素构成1个逆序对。
共有逆序对: n − 1 + n − 2 + . . . + 1 = n ( n − 1 ) 2 n-1+n-2+...+1 = \frac{n(n-1)}{2} n1+n2+...+1=2n(n1)
当n为 1 0 5 10^5 105时,逆序对最多有约为 1 0 10 10^{10} 1010个,超出了int可以表示的范围,所以保存逆序对的变量应该设为long long

【题解代码】

解法1:

ybt 1311,1237 OpenJudge 2.4 7622几题 数组长度N可以设为100005
洛谷 P1908 数组长度N必须设为500005

#include<bits/stdc++.h>
using namespace std;
#define N 500005//数组长度
int a[N], t[N];
long long ct;//逆序数 
void mergeSort(int l, int r)
{
    if(l >= r)
        return;
    int mid = (l + r)/2;
    mergeSort(l, mid);
    mergeSort(mid+1, r);
    int ti = l, li = l, ri = mid+1;//li第一个段数组中的下标 ri第二段数组中的下标 ti临时数组中的下标
    while(li <= mid && ri <= r)
    {
        if(a[li] <= a[ri])
            t[ti++] = a[li++];
        else//如果a[ri] < a[li] 此时a[ri]与左侧数组剩余的mid-li+1个数字构成逆序对 
        {
            ct += mid-li+1;
            t[ti++] = a[ri++];
        }
    }
    while(li <= mid)
        t[ti++] = a[li++];
    while(ri <= r)
        t[ti++] = a[ri++];
    for(int i = l; i <= r; ++i)
        a[i] = t[i];
}
int main()
{
    int n;
    cin >> n;
    for(int i = 1; i <= n; ++i)
        cin >> a[i];
    mergeSort(1, n);
    cout << ct;
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值