逆序对 题解 归并排序

文章介绍了一个编程问题,通过归并排序计算初始序列的逆序对,然后分析交换操作对逆序对的影响,得出最终最少逆序对的数量。

逆序对

题目描述

ZZR 有一个序列 a1,a2,⋯,an,他允许最多进行 k 次操作,每次操作交换两个相邻元素。
求经过变换后的序列中最少还有多少逆序对。
逆序对指的是二元组 (i,j),其满足 i<jai>aj

输入描述

第一行包含两个数 n,k,表示序列长度和交换两个相邻元素的次数上限。
第二行有 n 个数,表示序列 a1,a2,⋯,an。

输出描述

一个数表示最少的逆序对的数量。

用例输入 1

3 1
2 2 1

用例输出 1

1

用例输入 2

3 0
2 2 1

用例输出 2

2

数据规模与约定

1≤n≤105,0≤k≤109,1≤ai≤109。1≤n≤10^5,0≤k≤10^9,1≤a_i≤10^9。1n1050k1091ai109

思路

我们知道归并排序可以计算逆序对数量,而在逆序对存在的情况下,每次交换某个具有相邻元素的逆序对所对应的元素都可以使得逆序对减少一个。那么只要求出逆序对的数量,本题答案就显而易见了

代码

#include <bits/stdc++.h>
using namespace std;
#define max_Heap(x) priority_queue<x, vector<x>, less<x>>
#define min_Heap(x) priority_queue<x, vector<x>, greater<x>>
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> PII;
typedef pair<long long, long long> PLL;

const int maxn = 1e5 + 10;
int q[maxn], tmp[maxn];
ll ans;
void merge_sort(int q[], int l, int r)
{
    if (l >= r)
        return; // 如果l>=r,无需排序
    int mid = (l + r) / 2;
    merge_sort(q, l, mid);     // 分解左序列
    merge_sort(q, mid + 1, r); // 分解右序列
    int k = l, 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 (int i = l; i <= r; i++)
        q[i] = tmp[i];
}
int main()
{
    ios::sync_with_stdio(0);
    cin.tie(0);
    cout.tie(0);

    // 如果序列存在逆序对,则每交换两个元素,都可以使得逆序对减少一个
    int n, k;
    cin >> n >> k;
    for (int i = 1; i <= n; i++)
    {
        cin >> q[i];
    }
    merge_sort(q, 1, n);
    if (k >= ans) // 如果可交换次数大于等于逆序对数量,则可以将逆序对减少为0
    {
        cout << 0 << '\n';
    }
    else // 如果逆序对比交换次数多,答案为逆序对数量减去可交换次数
    {
        cout << ans - k << '\n';
    }

    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值